Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA-cmd.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA-cmd.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA-cmd.cpp	(revision 86838)
@@ -37,4 +37,8 @@
 #endif
 
+#ifdef DUMP_BITMAPS
+# include <iprt/formats/bmp.h>
+# include <stdio.h>
+#endif
 
 #if defined(LOG_ENABLED) || defined(VBOX_STRICT)
@@ -272,4 +276,427 @@
 
 
+#if !defined(VMSVGA3D_DX)
+/*
+ * Stubs for old backends.
+ */
+int vmsvga3dScreenTargetBind(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, uint32_t sid)
+{
+    RT_NOREF(pThisCC, pScreen, sid);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int vmsvga3dScreenTargetUpdate(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, SVGA3dRect const *pRect)
+{
+    RT_NOREF(pThisCC, pScreen, pRect);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int vmsvga3dSurfaceMap(PVGASTATECC pThisCC, SVGA3dSurfaceImageId const *pImage, SVGA3dBox const *pBox, VMSVGA3D_SURFACE_MAP enmMapType, VMSVGA3D_MAPPED_SURFACE *pMap)
+{
+    RT_NOREF(pThisCC, pImage, pBox, enmMapType, pMap);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int vmsvga3dSurfaceUnmap(PVGASTATECC pThisCC, SVGA3dSurfaceImageId const *pImage, VMSVGA3D_MAPPED_SURFACE *pMap, bool fWritten)
+{
+    RT_NOREF(pThisCC, pImage, pMap, fWritten);
+    return VERR_NOT_IMPLEMENTED;
+}
+#endif
+
+
+/*
+ *
+ * Guest-Backed Objects (GBO).
+ *
+ */
+
+static int vmsvgaR3GboCreate(PVMSVGAR3STATE pSvgaR3State, SVGAMobFormat ptDepth, PPN64 baseAddress, uint32_t sizeInBytes, bool fGCPhys64, PVMSVGAGBO pGbo)
+{
+    ASSERT_GUEST_RETURN(sizeInBytes <= _128M, VERR_INVALID_PARAMETER); /** @todo Less than SVGA_REG_MOB_MAX_SIZE */
+
+    /*
+     * The 'baseAddress' is a page number and points to the 'root page' of the GBO.
+     * Content of the root page depends on the ptDepth value:
+     * SVGA3D_MOBFMT_PTDEPTH[64]_0 - the only data page;
+     * SVGA3D_MOBFMT_PTDEPTH[64]_1 - array of page numbers for data pages;
+     * SVGA3D_MOBFMT_PTDEPTH[64]_2 - array of page numbers for SVGA3D_MOBFMT_PTDEPTH[64]_1 pages.
+     * The code below extracts the page addresses of the GBO.
+     */
+
+    /* Verify and normalize the ptDepth value. */
+    if (RT_LIKELY(ptDepth >= SVGA3D_MOBFMT_PTDEPTH64_0 && ptDepth <= SVGA3D_MOBFMT_PTDEPTH64_2))
+        ASSERT_GUEST_RETURN(fGCPhys64, VERR_INVALID_PARAMETER);
+    else if (ptDepth >= SVGA3D_MOBFMT_PTDEPTH_0 && ptDepth <= SVGA3D_MOBFMT_PTDEPTH_2)
+    {
+        ASSERT_GUEST_RETURN(!fGCPhys64, VERR_INVALID_PARAMETER);
+        /* Shift ptDepth to the SVGA3D_MOBFMT_PTDEPTH64_x range. */
+        ptDepth = (SVGAMobFormat)(ptDepth + SVGA3D_MOBFMT_PTDEPTH64_0 - SVGA3D_MOBFMT_PTDEPTH_0);
+    }
+    else if (ptDepth == SVGA3D_MOBFMT_RANGE)
+    { }
+    else
+        ASSERT_GUEST_FAILED_RETURN(VERR_INVALID_PARAMETER);
+
+    uint32_t const cPPNsPerPage = X86_PAGE_SIZE / (fGCPhys64 ? sizeof(PPN64) : sizeof(PPN));
+
+    pGbo->cbTotal = sizeInBytes;
+    pGbo->cTotalPages = (sizeInBytes + X86_PAGE_SIZE - 1) >> X86_PAGE_SHIFT;
+
+    /* Allocate the maximum amount possible (everything non-continuous) */
+    PVMSVGAGBODESCRIPTOR paDescriptors = (PVMSVGAGBODESCRIPTOR)RTMemAlloc(pGbo->cTotalPages * sizeof(VMSVGAGBODESCRIPTOR));
+    AssertReturn(paDescriptors, VERR_NO_MEMORY);
+
+    int rc = VINF_SUCCESS;
+    if (ptDepth == SVGA3D_MOBFMT_PTDEPTH64_0)
+    {
+        ASSERT_GUEST_STMT_RETURN(pGbo->cTotalPages == 1,
+                                 RTMemFree(paDescriptors),
+                                 VERR_INVALID_PARAMETER);
+
+        RTGCPHYS GCPhys = (RTGCPHYS)baseAddress << X86_PAGE_SHIFT;
+        GCPhys &= UINT64_C(0x00000FFFFFFFFFFF); /* Seeing rubbish in the top bits with certain linux guests. */
+        paDescriptors[0].GCPhys   = GCPhys;
+        paDescriptors[0].cPages = 1;
+    }
+    else if (ptDepth == SVGA3D_MOBFMT_PTDEPTH64_1)
+    {
+        ASSERT_GUEST_STMT_RETURN(pGbo->cTotalPages <= cPPNsPerPage,
+                                 RTMemFree(paDescriptors),
+                                 VERR_INVALID_PARAMETER);
+
+        /* Read the root page. */
+        uint8_t au8RootPage[X86_PAGE_SIZE];
+        RTGCPHYS GCPhys = (RTGCPHYS)baseAddress << X86_PAGE_SHIFT;
+        rc = PDMDevHlpPCIPhysRead(pSvgaR3State->pDevIns, GCPhys, &au8RootPage, sizeof(au8RootPage));
+        if (RT_SUCCESS(rc))
+        {
+            PPN64 *paPPN64 = (PPN64 *)&au8RootPage[0];
+            PPN *paPPN32 = (PPN *)&au8RootPage[0];
+            for (uint32_t iPPN = 0; iPPN < pGbo->cTotalPages; ++iPPN)
+            {
+                GCPhys = (RTGCPHYS)(fGCPhys64 ? paPPN64[iPPN] : paPPN32[iPPN]) << X86_PAGE_SHIFT;
+                GCPhys &= UINT64_C(0x00000FFFFFFFFFFF); /* Seeing rubbish in the top bits with certain linux guests. */
+                paDescriptors[iPPN].GCPhys   = GCPhys;
+                paDescriptors[iPPN].cPages = 1;
+            }
+        }
+    }
+    else if (ptDepth == SVGA3D_MOBFMT_PTDEPTH64_2)
+    {
+        ASSERT_GUEST_STMT_RETURN(pGbo->cTotalPages <= cPPNsPerPage * cPPNsPerPage,
+                                 RTMemFree(paDescriptors),
+                                 VERR_INVALID_PARAMETER);
+
+        /* Read the Level2 root page. */
+        uint8_t au8RootPageLevel2[X86_PAGE_SIZE];
+        RTGCPHYS GCPhys = (RTGCPHYS)baseAddress << X86_PAGE_SHIFT;
+        rc = PDMDevHlpPCIPhysRead(pSvgaR3State->pDevIns, GCPhys, &au8RootPageLevel2, sizeof(au8RootPageLevel2));
+        if (RT_SUCCESS(rc))
+        {
+            uint32_t cPagesLeft = pGbo->cTotalPages;
+
+            PPN64 *paPPN64Level2 = (PPN64 *)&au8RootPageLevel2[0];
+            PPN *paPPN32Level2 = (PPN *)&au8RootPageLevel2[0];
+
+            uint32_t const cPPNsLevel2 = (pGbo->cTotalPages + cPPNsPerPage - 1) / cPPNsPerPage;
+            for (uint32_t iPPNLevel2 = 0; iPPNLevel2 < cPPNsLevel2; ++iPPNLevel2)
+            {
+                /* Read the Level1 root page. */
+                uint8_t au8RootPage[X86_PAGE_SIZE];
+                RTGCPHYS GCPhysLevel1 = (RTGCPHYS)(fGCPhys64 ? paPPN64Level2[iPPNLevel2] : paPPN32Level2[iPPNLevel2]) << X86_PAGE_SHIFT;
+                GCPhys &= UINT64_C(0x00000FFFFFFFFFFF); /* Seeing rubbish in the top bits with certain linux guests. */
+                rc = PDMDevHlpPCIPhysRead(pSvgaR3State->pDevIns, GCPhysLevel1, &au8RootPage, sizeof(au8RootPage));
+                if (RT_SUCCESS(rc))
+                {
+                    PPN64 *paPPN64 = (PPN64 *)&au8RootPage[0];
+                    PPN *paPPN32 = (PPN *)&au8RootPage[0];
+
+                    uint32_t const cPPNs = RT_MIN(cPagesLeft, cPPNsPerPage);
+                    for (uint32_t iPPN = 0; iPPN < cPPNs; ++iPPN)
+                    {
+                        GCPhys = (RTGCPHYS)(fGCPhys64 ? paPPN64[iPPN] : paPPN32[iPPN]) << X86_PAGE_SHIFT;
+                        GCPhys &= UINT64_C(0x00000FFFFFFFFFFF); /* Seeing rubbish in the top bits with certain linux guests. */
+                        paDescriptors[iPPN + iPPNLevel2 * cPPNsPerPage].GCPhys   = GCPhys;
+                        paDescriptors[iPPN + iPPNLevel2 * cPPNsPerPage].cPages = 1;
+                    }
+                    cPagesLeft -= cPPNs;
+                }
+            }
+        }
+    }
+    else if (ptDepth == SVGA3D_MOBFMT_RANGE)
+    {
+        RTGCPHYS GCPhys = (RTGCPHYS)baseAddress << X86_PAGE_SHIFT;
+        GCPhys &= UINT64_C(0x00000FFFFFFFFFFF); /* Seeing rubbish in the top bits with certain linux guests. */
+        paDescriptors[0].GCPhys = GCPhys;
+        paDescriptors[0].cPages = pGbo->cTotalPages;
+    }
+    else
+    {
+        AssertFailed();
+        return VERR_INTERNAL_ERROR; /* ptDepth should be already verified. */
+    }
+
+    /* Compress the descriptors. */
+    if (ptDepth != SVGA3D_MOBFMT_RANGE)
+    {
+        uint32_t iDescriptor = 0;
+        for (uint32_t i = 1; i < pGbo->cTotalPages; ++i)
+        {
+            /* Continuous physical memory? */
+            if (paDescriptors[i].GCPhys == paDescriptors[iDescriptor].GCPhys + paDescriptors[iDescriptor].cPages * X86_PAGE_SIZE)
+            {
+                Assert(paDescriptors[iDescriptor].cPages);
+                paDescriptors[iDescriptor].cPages++;
+                Log5Func(("Page %x GCPhys=%RGp successor\n", i, paDescriptors[i].GCPhys));
+            }
+            else
+            {
+                iDescriptor++;
+                paDescriptors[iDescriptor].GCPhys   = paDescriptors[i].GCPhys;
+                paDescriptors[iDescriptor].cPages = 1;
+                Log5Func(("Page %x GCPhys=%RGp\n", i, paDescriptors[iDescriptor].GCPhys));
+            }
+        }
+
+        pGbo->cDescriptors = iDescriptor + 1;
+        Log5Func(("Nr of descriptors %d\n", pGbo->cDescriptors));
+    }
+    else
+        pGbo->cDescriptors = 1;
+
+    if (RT_LIKELY(pGbo->cDescriptors < pGbo->cTotalPages))
+    {
+        pGbo->paDescriptors = (PVMSVGAGBODESCRIPTOR)RTMemRealloc(paDescriptors, pGbo->cDescriptors * sizeof(VMSVGAGBODESCRIPTOR));
+        AssertReturn(pGbo->paDescriptors, VERR_NO_MEMORY);
+    }
+    else
+        pGbo->paDescriptors = paDescriptors;
+
+    return VINF_SUCCESS;
+}
+
+
+static void vmsvgaR3GboDestroy(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGbo)
+{
+    RT_NOREF(pSvgaR3State);
+    if (RT_LIKELY(VMSVGA_IS_GBO_CREATED(pGbo)))
+    {
+        RTMemFree(pGbo->paDescriptors);
+        RT_ZERO(pGbo);
+    }
+}
+
+
+typedef enum VMSVGAGboTransferDirection
+{
+    VMSVGAGboTransferDirection_Read,
+    VMSVGAGboTransferDirection_Write,
+} VMSVGAGboTransferDirection;
+
+static int vmsvgaR3GboTransfer(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGbo,
+                               uint32_t off, void *pvData, uint32_t cbData,
+                               VMSVGAGboTransferDirection enmDirection)
+{
+// ASMBreakpoint();
+    int rc = VINF_SUCCESS;
+    uint8_t *pu8CurrentHost  = (uint8_t *)pvData;
+
+    /* Find the right descriptor */
+    PCVMSVGAGBODESCRIPTOR const paDescriptors = pGbo->paDescriptors;
+    uint32_t iDescriptor = 0;                               /* Index in the descriptor array. */
+    uint32_t offDescriptor = 0;                             /* GMR offset of the current descriptor. */
+    while (offDescriptor + paDescriptors[iDescriptor].cPages * X86_PAGE_SIZE <= off)
+    {
+        offDescriptor += paDescriptors[iDescriptor].cPages * X86_PAGE_SIZE;
+        AssertReturn(offDescriptor < pGbo->cbTotal, VERR_INTERNAL_ERROR); /* overflow protection */
+        ++iDescriptor;
+        AssertReturn(iDescriptor < pGbo->cDescriptors, VERR_INTERNAL_ERROR);
+    }
+
+    while (cbData)
+    {
+        uint32_t cbToCopy;
+        if (off + cbData <= offDescriptor + paDescriptors[iDescriptor].cPages * X86_PAGE_SIZE)
+            cbToCopy = cbData;
+        else
+        {
+            cbToCopy = (offDescriptor + paDescriptors[iDescriptor].cPages * X86_PAGE_SIZE - off);
+            AssertReturn(cbToCopy <= cbData, VERR_INVALID_PARAMETER);
+        }
+
+        RTGCPHYS const GCPhys = paDescriptors[iDescriptor].GCPhys + off - offDescriptor;
+        Log5Func(("%s phys=%RGp\n", (enmDirection == VMSVGAGboTransferDirection_Read) ? "READ" : "WRITE", GCPhys));
+
+        if (enmDirection == VMSVGAGboTransferDirection_Read)
+            rc = PDMDevHlpPCIPhysRead(pSvgaR3State->pDevIns, GCPhys, pu8CurrentHost, cbToCopy);
+        else
+            rc = PDMDevHlpPCIPhysWrite(pSvgaR3State->pDevIns, GCPhys, pu8CurrentHost, cbToCopy);
+        AssertRCBreak(rc);
+
+        cbData         -= cbToCopy;
+        off            += cbToCopy;
+        pu8CurrentHost += cbToCopy;
+
+        /* Go to the next descriptor if there's anything left. */
+        if (cbData)
+        {
+            offDescriptor += paDescriptors[iDescriptor].cPages * X86_PAGE_SIZE;
+            AssertReturn(offDescriptor < pGbo->cbTotal, VERR_INTERNAL_ERROR);
+            ++iDescriptor;
+            AssertReturn(iDescriptor < pGbo->cDescriptors, VERR_INTERNAL_ERROR);
+        }
+    }
+    return rc;
+}
+
+
+static int vmsvgaR3GboWrite(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGbo,
+                            uint32_t off, void const *pvData, uint32_t cbData)
+{
+    return vmsvgaR3GboTransfer(pSvgaR3State, pGbo,
+                               off, (void *)pvData, cbData,
+                               VMSVGAGboTransferDirection_Write);
+}
+
+
+static int vmsvgaR3GboRead(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGbo,
+                           uint32_t off, void *pvData, uint32_t cbData)
+{
+    return vmsvgaR3GboTransfer(pSvgaR3State, pGbo,
+                               off, pvData, cbData,
+                               VMSVGAGboTransferDirection_Read);
+}
+
+
+/*
+ *
+ * Object Tables.
+ *
+ */
+
+static int vmsvgaR3OTableVerifyIndex(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGboOTable,
+                                     uint32_t idx, uint32_t cbEntry)
+{
+    RT_NOREF(pSvgaR3State);
+
+    /* The table must exist and the index must be within the table. */
+    ASSERT_GUEST_RETURN(VMSVGA_IS_GBO_CREATED(pGboOTable), VERR_INVALID_PARAMETER);
+    ASSERT_GUEST_RETURN(idx < pGboOTable->cbTotal / cbEntry, VERR_INVALID_PARAMETER);
+    RT_UNTRUSTED_VALIDATED_FENCE();
+    return VINF_SUCCESS;
+}
+
+
+static int vmsvgaR3OTableRead(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGboOTable,
+                              uint32_t idx, uint32_t cbEntry,
+                              void *pvData, uint32_t cbData)
+{
+    AssertReturn(cbData <= cbEntry, VERR_INVALID_PARAMETER);
+
+    int rc = vmsvgaR3OTableVerifyIndex(pSvgaR3State, pGboOTable, idx, cbEntry);
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t const off = idx * cbEntry;
+        rc = vmsvgaR3GboRead(pSvgaR3State, pGboOTable, off, pvData, cbData);
+    }
+    return rc;
+}
+
+static int vmsvgaR3OTableWrite(PVMSVGAR3STATE pSvgaR3State, PVMSVGAGBO pGboOTable,
+                               uint32_t idx, uint32_t cbEntry,
+                               void const *pvData, uint32_t cbData)
+{
+    AssertReturn(cbData <= cbEntry, VERR_INVALID_PARAMETER);
+
+    int rc = vmsvgaR3OTableVerifyIndex(pSvgaR3State, pGboOTable, idx, cbEntry);
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t const off = idx * cbEntry;
+        rc = vmsvgaR3GboWrite(pSvgaR3State, pGboOTable, off, pvData, cbData);
+    }
+    return rc;
+}
+
+
+/*
+ *
+ * The guest's Memory OBjects (MOB).
+ *
+ */
+
+static int vmsvgaR3MobCreate(PVMSVGAR3STATE pSvgaR3State,
+                             SVGAMobFormat ptDepth, PPN64 baseAddress, uint32_t sizeInBytes, SVGAMobId mobid,
+                             bool fGCPhys64, PVMSVGAMOB pMob)
+{
+    RT_ZERO(*pMob);
+
+    /* Update the entry in the pSvgaR3State->pGboOTableMob. */
+    SVGAOTableMobEntry entry;
+    entry.ptDepth     = ptDepth;
+    entry.sizeInBytes = sizeInBytes;
+    entry.base        = baseAddress;
+    int rc = vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableMob,
+                                 mobid, SVGA3D_OTABLE_MOB_ENTRY_SIZE, &entry, sizeof(entry));
+    if (RT_SUCCESS(rc))
+    {
+        /* Create the corresponding GBO. */
+        rc = vmsvgaR3GboCreate(pSvgaR3State, ptDepth, baseAddress, sizeInBytes, fGCPhys64, &pMob->Gbo);
+        if (RT_SUCCESS(rc))
+        {
+            /* Add to the tree of known GBOs and the LRU list. */
+            pMob->Core.Key = mobid;
+            if (RTAvlU32Insert(&pSvgaR3State->MOBTree, &pMob->Core))
+            {
+                RTListPrepend(&pSvgaR3State->MOBLRUList, &pMob->nodeLRU);
+                return VINF_SUCCESS;
+            }
+
+            vmsvgaR3GboDestroy(pSvgaR3State, &pMob->Gbo);
+        }
+    }
+
+    return rc;
+}
+
+
+static int vmsvgaR3MobDestroy(PVMSVGAR3STATE pSvgaR3State, SVGAMobId mobid)
+{
+    /* Update the entry in the pSvgaR3State->pGboOTableMob. */
+    SVGAOTableMobEntry entry;
+    RT_ZERO(entry);
+    vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableMob,
+                        mobid, SVGA3D_OTABLE_MOB_ENTRY_SIZE, &entry, sizeof(entry));
+
+    PVMSVGAMOB pMob = (PVMSVGAMOB)RTAvlU32Remove(&pSvgaR3State->MOBTree, mobid);
+    if (pMob)
+    {
+        RTListNodeRemove(&pMob->nodeLRU);
+        vmsvgaR3GboDestroy(pSvgaR3State, &pMob->Gbo);
+        RTMemFree(pMob);
+        return VINF_SUCCESS;
+    }
+
+    return VERR_INVALID_PARAMETER;
+}
+
+
+static PVMSVGAMOB vmsvgaR3MobGet(PVMSVGAR3STATE pSvgaR3State, SVGAMobId mobid)
+{
+    PVMSVGAMOB pMob = (PVMSVGAMOB)RTAvlU32Get(&pSvgaR3State->MOBTree, mobid);
+    if (pMob)
+    {
+        /* Move to the head of the LRU list. */
+        RTListNodeRemove(&pMob->nodeLRU);
+        RTListPrepend(&pSvgaR3State->MOBLRUList, &pMob->nodeLRU);
+    }
+
+    return pMob;
+}
+
+/*
+ * Screen objects.
+ */
 VMSVGASCREENOBJECT *vmsvgaR3GetScreenObject(PVGASTATECC pThisCC, uint32_t idScreen)
 {
@@ -435,4 +862,608 @@
 
 #ifdef VBOX_WITH_VMSVGA3D
+
+/*
+ * SVGA_3D_CMD_* handlers.
+ */
+
+
+/** SVGA_3D_CMD_SURFACE_DEFINE 1040, SVGA_3D_CMD_SURFACE_DEFINE_V2 1070
+ *
+ * @param pThisCC         The VGA/VMSVGA state for the current context.
+ * @param pCmd            The VMSVGA command.
+ * @param cMipLevelSizes  Number of elements in the paMipLevelSizes array.
+ * @param paMipLevelSizes Arrays of surface sizes for each face and miplevel.
+ */
+static void vmsvga3dCmdDefineSurface(PVGASTATECC pThisCC, SVGA3dCmdDefineSurface_v2 const *pCmd,
+                                     uint32_t cMipLevelSizes, SVGA3dSize *paMipLevelSizes)
+{
+    ASSERT_GUEST_RETURN_VOID(pCmd->sid < SVGA3D_MAX_SURFACE_IDS);
+    ASSERT_GUEST_RETURN_VOID(cMipLevelSizes >= 1);
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Number of faces (cFaces) is specified as the number of the first non-zero elements in the 'face' array.
+     * Since only plain surfaces (cFaces == 1) and cubemaps (cFaces == 6) are supported
+     * (see also SVGA3dCmdDefineSurface definition in svga3d_reg.h), we ignore anything else.
+     */
+    uint32_t cRemainingMipLevels = cMipLevelSizes;
+    uint32_t cFaces = 0;
+    for (uint32_t i = 0; i < SVGA3D_MAX_SURFACE_FACES; ++i)
+    {
+        if (pCmd->face[i].numMipLevels == 0)
+            break;
+
+        /* All SVGA3dSurfaceFace structures must have the same value of numMipLevels field */
+        ASSERT_GUEST_RETURN_VOID(pCmd->face[i].numMipLevels == pCmd->face[0].numMipLevels);
+
+        /* numMipLevels value can't be greater than the number of remaining elements in the paMipLevelSizes array. */
+        ASSERT_GUEST_RETURN_VOID(pCmd->face[i].numMipLevels <= cRemainingMipLevels);
+        cRemainingMipLevels -= pCmd->face[i].numMipLevels;
+
+        ++cFaces;
+    }
+    for (uint32_t i = cFaces; i < SVGA3D_MAX_SURFACE_FACES; ++i)
+        ASSERT_GUEST_RETURN_VOID(pCmd->face[i].numMipLevels == 0);
+
+    /* cFaces must be 6 for a cubemap and 1 otherwise. */
+    ASSERT_GUEST_RETURN_VOID(cFaces == (uint32_t)((pCmd->surfaceFlags & SVGA3D_SURFACE_CUBEMAP) ? 6 : 1));
+
+    /* Sum of face[i].numMipLevels must be equal to cMipLevels. */
+    ASSERT_GUEST_RETURN_VOID(cRemainingMipLevels == 0);
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Verify paMipLevelSizes */
+    uint32_t cWidth  = paMipLevelSizes[0].width;
+    uint32_t cHeight = paMipLevelSizes[0].height;
+    uint32_t cDepth  = paMipLevelSizes[0].depth;
+    for (uint32_t i = 1; i < pCmd->face[0].numMipLevels; ++i)
+    {
+        cWidth >>= 1;
+        if (cWidth == 0) cWidth = 1;
+        cHeight >>= 1;
+        if (cHeight == 0) cHeight = 1;
+        cDepth >>= 1;
+        if (cDepth == 0) cDepth = 1;
+        for (uint32_t iFace = 0; iFace < cFaces; ++iFace)
+        {
+            uint32_t const iMipLevelSize = iFace * pCmd->face[0].numMipLevels + i;
+            ASSERT_GUEST_RETURN_VOID(   cWidth  == paMipLevelSizes[iMipLevelSize].width
+                                     && cHeight == paMipLevelSizes[iMipLevelSize].height
+                                     && cDepth  == paMipLevelSizes[iMipLevelSize].depth);
+        }
+    }
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Create the surface. */
+    vmsvga3dSurfaceDefine(pThisCC, pCmd->sid, pCmd->surfaceFlags, pCmd->format,
+                          pCmd->multisampleCount, pCmd->autogenFilter,
+                          pCmd->face[0].numMipLevels, &paMipLevelSizes[0]);
+}
+
+
+/* SVGA_3D_CMD_DEFINE_GB_MOB 1093 */
+static void vmsvga3dCmdDefineGBMob(PVGASTATECC pThisCC, SVGA3dCmdDefineGBMob const *pCmd)
+{
+    ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    ASSERT_GUEST_RETURN_VOID(pCmd->mobid != SVGA_ID_INVALID); /* The guest should not use this id. */
+
+    /* Maybe just update the OTable and create Gbo when the MOB is actually accessed? */
+    /* Allocate a structure for the MOB. */
+    PVMSVGAMOB pMob = (PVMSVGAMOB)RTMemAllocZ(sizeof(*pMob));
+    AssertPtrReturnVoid(pMob);
+
+    int rc = vmsvgaR3MobCreate(pSvgaR3State, pCmd->ptDepth, pCmd->base, pCmd->sizeInBytes, pCmd->mobid, /*fGCPhys64=*/ false, pMob);
+    if (RT_SUCCESS(rc))
+    {
+        return;
+    }
+
+    AssertFailed();
+
+    RTMemFree(pMob);
+}
+
+
+/* SVGA_3D_CMD_DESTROY_GB_MOB 1094 */
+static void vmsvga3dCmdDestroyGBMob(PVGASTATECC pThisCC, SVGA3dCmdDestroyGBMob const *pCmd)
+{
+//    ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    ASSERT_GUEST_RETURN_VOID(pCmd->mobid != SVGA_ID_INVALID); /* The guest should not use this id. */
+
+    int rc = vmsvgaR3MobDestroy(pSvgaR3State, pCmd->mobid);
+    if (RT_SUCCESS(rc))
+    {
+        return;
+    }
+
+    AssertFailed();
+}
+
+
+/* SVGA_3D_CMD_DEFINE_GB_SURFACE 1097 */
+static void vmsvga3dCmdDefineGBSurface(PVGASTATECC pThisCC, SVGA3dCmdDefineGBSurface const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    /* Update the entry in the pSvgaR3State->pGboOTableSurface. */
+    SVGAOTableSurfaceEntry entry;
+    RT_ZERO(entry);
+    entry.format = pCmd->format;
+    entry.surfaceFlags = pCmd->surfaceFlags;
+    entry.numMipLevels = pCmd->numMipLevels;
+    entry.multisampleCount = pCmd->multisampleCount;
+    entry.autogenFilter = pCmd->autogenFilter;
+    entry.size = pCmd->size;
+    entry.mobid = SVGA_ID_INVALID;
+    // entry.arraySize = 0;
+    // entry.mobPitch = 0;
+    int rc = vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                                 pCmd->sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE, &entry, sizeof(entry));
+    if (RT_SUCCESS(rc))
+    {
+        /* Create the host surface. */
+        /** @todo fGBO = true flag. */
+        vmsvga3dSurfaceDefine(pThisCC, pCmd->sid, pCmd->surfaceFlags, pCmd->format,
+                              pCmd->multisampleCount, pCmd->autogenFilter,
+                              pCmd->numMipLevels, &pCmd->size);
+    }
+}
+
+
+/* SVGA_3D_CMD_DESTROY_GB_SURFACE 1098 */
+static void vmsvga3dCmdDestroyGBSurface(PVGASTATECC pThisCC, SVGA3dCmdDestroyGBSurface const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    /* Update the entry in the pSvgaR3State->pGboOTableSurface. */
+    SVGAOTableSurfaceEntry entry;
+    RT_ZERO(entry);
+    entry.mobid = SVGA_ID_INVALID;
+    vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                        pCmd->sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE, &entry, sizeof(entry));
+
+    vmsvga3dSurfaceDestroy(pThisCC, pCmd->sid);
+}
+
+
+/* SVGA_3D_CMD_BIND_GB_SURFACE 1099 */
+static void vmsvga3dCmdBindGBSurface(PVGASTATECC pThisCC, SVGA3dCmdBindGBSurface const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+    RT_NOREF(pCmd, pSvgaR3State);
+
+    /* Assign the mobid to the surface. */
+    int rc = VINF_SUCCESS;
+    if (pCmd->mobid != SVGA_ID_INVALID)
+        rc = vmsvgaR3OTableVerifyIndex(pSvgaR3State, &pSvgaR3State->GboOTableMob,
+                                       pCmd->mobid, SVGA3D_OTABLE_MOB_ENTRY_SIZE);
+    if (RT_SUCCESS(rc))
+    {
+        SVGAOTableSurfaceEntry entry;
+        rc = vmsvgaR3OTableRead(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                                pCmd->sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE, &entry, sizeof(entry));
+        if (RT_SUCCESS(rc))
+        {
+            entry.mobid = pCmd->mobid;
+            rc = vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                                     pCmd->sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE, &entry, sizeof(entry));
+            if (RT_SUCCESS(rc))
+            {
+                /* */
+            }
+        }
+    }
+}
+
+
+#ifdef DUMP_BITMAPS
+static int vmsvga3dBmpWrite(const char *pszFilename, VMSVGA3D_MAPPED_SURFACE const *pMap)
+{
+    if (pMap->cbPixel != 4)
+        return VERR_NOT_SUPPORTED;
+
+    int const w = pMap->box.w;
+    int const h = pMap->box.h;
+
+    const int cbBitmap = w * h * 4;
+
+    FILE *f = fopen(pszFilename, "wb");
+    if (!f)
+        return VERR_FILE_NOT_FOUND;
+
+    {
+        BMPFILEHDR fileHdr;
+        RT_ZERO(fileHdr);
+        fileHdr.uType      = BMP_HDR_MAGIC;
+        fileHdr.cbFileSize = sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR) + cbBitmap;
+        fileHdr.offBits    = sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR);
+
+        BMPWIN3XINFOHDR coreHdr;
+        RT_ZERO(coreHdr);
+        coreHdr.cbSize      = sizeof(coreHdr);
+        coreHdr.uWidth      = w;
+        coreHdr.uHeight     = -h;
+        coreHdr.cPlanes     = 1;
+        coreHdr.cBits       = 32;
+        coreHdr.cbSizeImage = cbBitmap;
+
+        fwrite(&fileHdr, 1, sizeof(fileHdr), f);
+        fwrite(&coreHdr, 1, sizeof(coreHdr), f);
+    }
+
+    if (pMap->cbPixel == 4)
+    {
+        const uint8_t *s = (uint8_t *)pMap->pvData;
+        for (int32_t y = 0; y < h; ++y)
+        {
+            fwrite(s, 1, w * pMap->cbPixel, f);
+
+            s += pMap->cbRowPitch;
+        }
+    }
+
+    fclose(f);
+
+    return VINF_SUCCESS;
+}
+
+
+void vmsvga3dMapWriteBmpFile(VMSVGA3D_MAPPED_SURFACE const *pMap, char const *pszPrefix)
+{
+    static int idxBitmap = 0;
+    char *pszFilename = RTStrAPrintf2("bmp\\%s%d.bmp", pszPrefix, idxBitmap++);
+    vmsvga3dBmpWrite(pszFilename, pMap);
+    RTStrFree(pszFilename);
+}
+#endif /* DUMP_BITMAPS */
+
+
+/* SVGA_3D_CMD_UPDATE_GB_IMAGE 1101 */
+static void vmsvga3dCmdUpdateGBImage(PVGASTATECC pThisCC, SVGA3dCmdUpdateGBImage const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    LogFlowFunc(("sid=%u @%u,%u,%u %ux%ux%u\n",
+                 pCmd->image.sid, pCmd->box.x, pCmd->box.y, pCmd->box.z, pCmd->box.w, pCmd->box.h, pCmd->box.d));
+
+    /* "update a surface from its backing MOB." */
+    SVGAOTableSurfaceEntry entrySurface;
+    int rc = vmsvgaR3OTableRead(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                            pCmd->image.sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE, &entrySurface, sizeof(entrySurface));
+    if (RT_SUCCESS(rc))
+    {
+        PVMSVGAMOB pMob = vmsvgaR3MobGet(pSvgaR3State, entrySurface.mobid);
+        if (pMob)
+        {
+            VMSVGA3D_MAPPED_SURFACE map;
+            rc = vmsvga3dSurfaceMap(pThisCC, &pCmd->image, &pCmd->box, VMSVGA3D_SURFACE_MAP_WRITE_DISCARD, &map);
+            if (RT_SUCCESS(rc))
+            {
+                /* Copy MOB -> mapped surface. */
+                uint32_t offSrc = pCmd->box.x * map.cbPixel
+                                + pCmd->box.y * entrySurface.size.width * map.cbPixel
+                                + pCmd->box.z * entrySurface.size.height * entrySurface.size.width * map.cbPixel;
+                uint8_t *pu8Dst = (uint8_t *)map.pvData;
+                for (uint32_t z = 0; z < pCmd->box.d; ++z)
+                {
+                    for (uint32_t y = 0; y < pCmd->box.h; ++y)
+                    {
+                        rc = vmsvgaR3GboRead(pSvgaR3State, &pMob->Gbo, offSrc, pu8Dst, pCmd->box.w * map.cbPixel);
+                        if (RT_FAILURE(rc))
+                            break;
+
+                        pu8Dst += map.cbRowPitch;
+                        offSrc += entrySurface.size.width * map.cbPixel;
+                    }
+
+                    pu8Dst += map.cbDepthPitch;
+                    offSrc += entrySurface.size.height * entrySurface.size.width * map.cbPixel;
+                }
+
+                // vmsvga3dMapWriteBmpFile(&map, "Dynamic");
+
+                vmsvga3dSurfaceUnmap(pThisCC, &pCmd->image, &map, /* fWritten =  */true);
+            }
+        }
+    }
+}
+
+
+/* SVGA_3D_CMD_INVALIDATE_GB_SURFACE 1106 */
+static void vmsvga3dCmdInvalidateGBSurface(PVGASTATECC pThisCC, SVGA3dCmdInvalidateGBSurface const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+    RT_NOREF(pSvgaR3State, pCmd);
+    /** @todo Implement. */
+}
+
+
+/* SVGA_3D_CMD_SET_OTABLE_BASE64 1115 */
+static void vmsvga3dCmdSetOTableBase64(PVGASTATECC pThisCC, SVGA3dCmdSetOTableBase64 const *pCmd)
+{
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    /*
+     * Create a GBO for the table.
+     */
+    PVMSVGAGBO pGbo;
+    switch (pCmd->type)
+    {
+        case SVGA_OTABLE_MOB:
+        {
+            pGbo = &pSvgaR3State->GboOTableMob;
+            break;
+        }
+        case SVGA_OTABLE_SURFACE:
+        {
+            pGbo = &pSvgaR3State->GboOTableSurface;
+            break;
+        }
+        case SVGA_OTABLE_CONTEXT:
+        {
+            pGbo = &pSvgaR3State->GboOTableContext;
+            break;
+        }
+        case SVGA_OTABLE_SHADER:
+        {
+            pGbo = &pSvgaR3State->GboOTableShader;
+            break;
+        }
+        case SVGA_OTABLE_SCREENTARGET:
+        {
+            pGbo = &pSvgaR3State->GboOTableScreenTarget;
+            break;
+        }
+        default:
+            pGbo = NULL;
+            ASSERT_GUEST_FAILED();
+            break;
+    }
+
+    if (pGbo)
+    {
+        /* Recreate. */
+        vmsvgaR3GboDestroy(pSvgaR3State, pGbo);
+        int rc = vmsvgaR3GboCreate(pSvgaR3State, pCmd->ptDepth, pCmd->baseAddress, pCmd->sizeInBytes, /*fGCPhys64=*/ true, pGbo);
+        AssertRC(rc);
+    }
+}
+
+
+/* SVGA_3D_CMD_DEFINE_GB_SCREENTARGET 1124 */
+static void vmsvga3dCmdDefineGBScreenTarget(PVGASTATE pThis, PVGASTATECC pThisCC, SVGA3dCmdDefineGBScreenTarget const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    ASSERT_GUEST_RETURN_VOID(pCmd->stid < RT_ELEMENTS(pSvgaR3State->aScreens));
+    ASSERT_GUEST_RETURN_VOID(pCmd->width > 0 && pCmd->width <= pThis->svga.u32MaxWidth); /* SVGA_REG_SCREENTARGET_MAX_WIDTH */
+    ASSERT_GUEST_RETURN_VOID(pCmd->height > 0 && pCmd->height <= pThis->svga.u32MaxHeight); /* SVGA_REG_SCREENTARGET_MAX_HEIGHT */
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Update the entry in the pSvgaR3State->pGboOTableScreenTarget. */
+    SVGAOTableScreenTargetEntry entry;
+    RT_ZERO(entry);
+    entry.image.sid = SVGA_ID_INVALID;
+    // entry.image.face = 0;
+    // entry.image.mipmap = 0;
+    entry.width = pCmd->width;
+    entry.height = pCmd->height;
+    entry.xRoot = pCmd->xRoot;
+    entry.yRoot = pCmd->yRoot;
+    entry.flags = pCmd->flags;
+    entry.dpi = pCmd->dpi;
+    int rc = vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableScreenTarget,
+                                 pCmd->stid, SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE, &entry, sizeof(entry));
+    if (RT_SUCCESS(rc))
+    {
+        /* Screen objects and screen targets are similar, therefore we will use the same for both. */
+        /** @todo Generic screen object/target interface. */
+        VMSVGASCREENOBJECT *pScreen = &pSvgaR3State->aScreens[pCmd->stid];
+        pScreen->fDefined  = true;
+        pScreen->fModified = true;
+        pScreen->fuScreen  = SVGA_SCREEN_MUST_BE_SET
+                           | (RT_BOOL(pCmd->flags & SVGA_STFLAG_PRIMARY) ? SVGA_SCREEN_IS_PRIMARY : 0);
+        pScreen->idScreen  = pCmd->stid;
+
+        pScreen->xOrigin = pCmd->xRoot;
+        pScreen->yOrigin = pCmd->yRoot;
+        pScreen->cWidth  = pCmd->width;
+        pScreen->cHeight = pCmd->height;
+        pScreen->offVRAM = 0; /* Always for screen targets, they use either a separate memory buffer or a host window. */
+        pScreen->cbPitch = pCmd->width * 4;
+        pScreen->cBpp    = 32;
+
+        if (RT_LIKELY(pThis->svga.f3DEnabled))
+            vmsvga3dDefineScreen(pThis, pThisCC, pScreen);
+
+        if (!pScreen->pHwScreen)
+        {
+            /* System memory buffer. */
+            pScreen->pvScreenBitmap = RTMemAllocZ(pScreen->cHeight * pScreen->cbPitch);
+        }
+
+        pThis->svga.fGFBRegisters = false;
+        vmsvgaR3ChangeMode(pThis, pThisCC);
+    }
+}
+
+
+/* SVGA_3D_CMD_DESTROY_GB_SCREENTARGET 1125 */
+static void vmsvga3dCmdDestroyGBScreenTarget(PVGASTATE pThis, PVGASTATECC pThisCC, SVGA3dCmdDestroyGBScreenTarget const *pCmd)
+{
+//    ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    ASSERT_GUEST_RETURN_VOID(pCmd->stid < RT_ELEMENTS(pSvgaR3State->aScreens));
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Update the entry in the pSvgaR3State->pGboOTableScreenTarget. */
+    SVGAOTableScreenTargetEntry entry;
+    RT_ZERO(entry);
+    int rc = vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableScreenTarget,
+                                 pCmd->stid, SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE, &entry, sizeof(entry));
+    if (RT_SUCCESS(rc))
+    {
+        /* Screen objects and screen targets are similar, therefore we will use the same for both. */
+        /** @todo Generic screen object/target interface. */
+        VMSVGASCREENOBJECT *pScreen = &pSvgaR3State->aScreens[pCmd->stid];
+        pScreen->fModified = true;
+        pScreen->fDefined  = false;
+        pScreen->idScreen  = pCmd->stid;
+
+        if (RT_LIKELY(pThis->svga.f3DEnabled))
+            vmsvga3dDestroyScreen(pThisCC, pScreen);
+
+        vmsvgaR3ChangeMode(pThis, pThisCC);
+
+        RTMemFree(pScreen->pvScreenBitmap);
+        pScreen->pvScreenBitmap = NULL;
+    }
+}
+
+
+/* SVGA_3D_CMD_BIND_GB_SCREENTARGET 1126 */
+static void vmsvga3dCmdBindGBScreenTarget(PVGASTATECC pThisCC, SVGA3dCmdBindGBScreenTarget const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    /* "Binding a surface to a Screen Target the same as flipping" */
+
+    ASSERT_GUEST_RETURN_VOID(pCmd->stid < RT_ELEMENTS(pSvgaR3State->aScreens));
+    ASSERT_GUEST_RETURN_VOID(pCmd->image.face == 0 && pCmd->image.mipmap == 0);
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Assign the surface to the screen target. */
+    int rc = VINF_SUCCESS;
+    if (pCmd->image.sid != SVGA_ID_INVALID)
+        rc = vmsvgaR3OTableVerifyIndex(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                                       pCmd->image.sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE);
+    if (RT_SUCCESS(rc))
+    {
+        SVGAOTableScreenTargetEntry entry;
+        rc = vmsvgaR3OTableRead(pSvgaR3State, &pSvgaR3State->GboOTableScreenTarget,
+                                pCmd->stid, SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE, &entry, sizeof(entry));
+        if (RT_SUCCESS(rc))
+        {
+            entry.image = pCmd->image;
+            rc = vmsvgaR3OTableWrite(pSvgaR3State, &pSvgaR3State->GboOTableScreenTarget,
+                                     pCmd->stid, SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE, &entry, sizeof(entry));
+            if (RT_SUCCESS(rc))
+            {
+                VMSVGASCREENOBJECT *pScreen = &pSvgaR3State->aScreens[pCmd->stid];
+                rc = vmsvga3dScreenTargetBind(pThisCC, pScreen, pCmd->image.sid);
+                AssertRC(rc);
+            }
+        }
+    }
+}
+
+
+/* SVGA_3D_CMD_UPDATE_GB_SCREENTARGET 1127 */
+static void vmsvga3dCmdUpdateGBScreenTarget(PVGASTATECC pThisCC, SVGA3dCmdUpdateGBScreenTarget const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    /* Update the screen target from its backing surface. */
+    ASSERT_GUEST_RETURN_VOID(pCmd->stid < RT_ELEMENTS(pSvgaR3State->aScreens));
+    RT_UNTRUSTED_VALIDATED_FENCE();
+
+    /* Get the screen target info. */
+    SVGAOTableScreenTargetEntry entryScreenTarget;
+    int rc = vmsvgaR3OTableRead(pSvgaR3State, &pSvgaR3State->GboOTableScreenTarget,
+                                pCmd->stid, SVGA3D_OTABLE_SCREEN_TARGET_ENTRY_SIZE, &entryScreenTarget, sizeof(entryScreenTarget));
+    if (RT_SUCCESS(rc))
+    {
+        ASSERT_GUEST_RETURN_VOID(entryScreenTarget.image.face == 0 && entryScreenTarget.image.mipmap == 0);
+        RT_UNTRUSTED_VALIDATED_FENCE();
+
+        if (entryScreenTarget.image.sid != SVGA_ID_INVALID)
+        {
+            SVGAOTableSurfaceEntry entrySurface;
+            rc = vmsvgaR3OTableRead(pSvgaR3State, &pSvgaR3State->GboOTableSurface,
+                                    entryScreenTarget.image.sid, SVGA3D_OTABLE_SURFACE_ENTRY_SIZE, &entrySurface, sizeof(entrySurface));
+            if (RT_SUCCESS(rc))
+            {
+                /* Copy entrySurface.mobid content to the screen target. */
+                if (entrySurface.mobid != SVGA_ID_INVALID)
+                {
+                    RT_UNTRUSTED_VALIDATED_FENCE();
+                    SVGA3dRect targetRect = pCmd->rect;
+
+                    VMSVGASCREENOBJECT *pScreen = &pSvgaR3State->aScreens[pCmd->stid];
+                    if (pScreen->pHwScreen)
+                    {
+                        /* Copy the screen target surface to the backend's screen. */
+                        vmsvga3dScreenTargetUpdate(pThisCC, pScreen, &targetRect);
+                    }
+                    else if (pScreen->pvScreenBitmap)
+                    {
+                        /* Copy the screen target surface to the memory buffer. */
+                        VMSVGA3D_MAPPED_SURFACE map;
+                        rc = vmsvga3dSurfaceMap(pThisCC, &entryScreenTarget.image, NULL, VMSVGA3D_SURFACE_MAP_READ, &map);
+                        if (RT_SUCCESS(rc))
+                        {
+                            uint8_t const *pu8Src = (uint8_t *)map.pvData
+                                                  + targetRect.x * map.cbPixel
+                                                  + targetRect.y * map.cbRowPitch;
+                            uint8_t *pu8Dst = (uint8_t *)pScreen->pvScreenBitmap
+                                            + targetRect.x * map.cbPixel
+                                            + targetRect.y * map.box.w * map.cbPixel;
+                            for (uint32_t y = 0; y < targetRect.h; ++y)
+                            {
+                                memcpy(pu8Dst, pu8Src, targetRect.w * map.cbPixel);
+
+                                pu8Src += map.cbRowPitch;
+                                pu8Dst += map.box.w * map.cbPixel;
+                            }
+
+                            vmsvga3dSurfaceUnmap(pThisCC, &entryScreenTarget.image, &map, /* fWritten =  */ false);
+
+                            vmsvgaR3UpdateScreen(pThisCC, pScreen, pCmd->rect.x, pCmd->rect.y, pCmd->rect.w, pCmd->rect.h);
+                        }
+                        else
+                            AssertFailed();
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+/* SVGA_3D_CMD_DEFINE_GB_MOB64 1135 */
+static void vmsvga3dCmdDefineGBMob64(PVGASTATECC pThisCC, SVGA3dCmdDefineGBMob64 const *pCmd)
+{
+//     ASMBreakpoint();
+    PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State;
+
+    ASSERT_GUEST_RETURN_VOID(pCmd->mobid != SVGA_ID_INVALID); /* The guest should not use this id. */
+
+    /* Maybe just update the OTable and create Gbo when the MOB is actually accessed? */
+    /* Allocate a structure for the MOB. */
+    PVMSVGAMOB pMob = (PVMSVGAMOB)RTMemAllocZ(sizeof(*pMob));
+    AssertPtrReturnVoid(pMob);
+
+    int rc = vmsvgaR3MobCreate(pSvgaR3State, pCmd->ptDepth, pCmd->base, pCmd->sizeInBytes, pCmd->mobid, /*fGCPhys64=*/ true, pMob);
+    if (RT_SUCCESS(rc))
+    {
+        return;
+    }
+
+    RTMemFree(pMob);
+}
+
+
 /** @def VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK
  * Check that the 3D command has at least a_cbMin of payload bytes after the
@@ -473,7 +1504,14 @@
         STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3Cmd3dSurfaceDefine);
 
-        uint32_t const cMipLevels = (cbCmd - sizeof(*pCmd)) / sizeof(SVGA3dSize);
-        vmsvga3dSurfaceDefine(pThisCC, pCmd->sid, (uint32_t)pCmd->surfaceFlags, pCmd->format, pCmd->face, 0,
-                              SVGA3D_TEX_FILTER_NONE, cMipLevels, (SVGA3dSize *)(pCmd + 1));
+        SVGA3dCmdDefineSurface_v2 cmd;
+        cmd.sid              = pCmd->sid;
+        cmd.surfaceFlags     = pCmd->surfaceFlags;
+        cmd.format           = pCmd->format;
+        memcpy(cmd.face, pCmd->face, sizeof(cmd.face));
+        cmd.multisampleCount = 0;
+        cmd.autogenFilter    = SVGA3D_TEX_FILTER_NONE;
+
+        uint32_t const cMipLevelSizes = (cbCmd - sizeof(*pCmd)) / sizeof(SVGA3dSize);
+        vmsvga3dCmdDefineSurface(pThisCC, &cmd, cMipLevelSizes, (SVGA3dSize *)(pCmd + 1));
 # ifdef DEBUG_GMR_ACCESS
         VMR3ReqCallWaitU(PDMDevHlpGetUVM(pDevIns), VMCPUID_ANY, (PFNRT)vmsvgaR3ResetGmrHandlers, 1, pThis);
@@ -488,8 +1526,9 @@
         STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3Cmd3dSurfaceDefineV2);
 
-        uint32_t const cMipLevels = (cbCmd - sizeof(*pCmd)) / sizeof(SVGA3dSize);
-        vmsvga3dSurfaceDefine(pThisCC, pCmd->sid, pCmd->surfaceFlags, pCmd->format, pCmd->face,
-                              pCmd->multisampleCount, pCmd->autogenFilter,
-                              cMipLevels, (SVGA3dSize *)(pCmd + 1));
+        uint32_t const cMipLevelSizes = (cbCmd - sizeof(*pCmd)) / sizeof(SVGA3dSize);
+        vmsvga3dCmdDefineSurface(pThisCC, pCmd, cMipLevelSizes, (SVGA3dSize *)(pCmd + 1));
+# ifdef DEBUG_GMR_ACCESS
+        VMR3ReqCallWaitU(PDMDevHlpGetUVM(pDevIns), VMCPUID_ANY, (PFNRT)vmsvgaR3ResetGmrHandlers, 1, pThis);
+# endif
         break;
     }
@@ -971,5 +2010,5 @@
         SVGA3dCmdDestroyGBMob *pCmd = (SVGA3dCmdDestroyGBMob *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdDestroyGBMob(pThisCC, pCmd);
         break;
     }
@@ -993,5 +2032,5 @@
         SVGA3dCmdDefineGBSurface *pCmd = (SVGA3dCmdDefineGBSurface *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdDefineGBSurface(pThisCC, pCmd);
         break;
     }
@@ -1001,5 +2040,5 @@
         SVGA3dCmdDestroyGBSurface *pCmd = (SVGA3dCmdDestroyGBSurface *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdDestroyGBSurface(pThisCC, pCmd);
         break;
     }
@@ -1009,5 +2048,5 @@
         SVGA3dCmdBindGBSurface *pCmd = (SVGA3dCmdBindGBSurface *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdBindGBSurface(pThisCC, pCmd);
         break;
     }
@@ -1025,5 +2064,5 @@
         SVGA3dCmdUpdateGBImage *pCmd = (SVGA3dCmdUpdateGBImage *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdUpdateGBImage(pThisCC, pCmd);
         break;
     }
@@ -1065,5 +2104,5 @@
         SVGA3dCmdInvalidateGBSurface *pCmd = (SVGA3dCmdInvalidateGBSurface *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdInvalidateGBSurface(pThisCC, pCmd);
         break;
     }
@@ -1137,5 +2176,5 @@
         SVGA3dCmdSetOTableBase64 *pCmd = (SVGA3dCmdSetOTableBase64 *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdSetOTableBase64(pThisCC, pCmd);
         break;
     }
@@ -1206,5 +2245,5 @@
         SVGA3dCmdDefineGBScreenTarget *pCmd = (SVGA3dCmdDefineGBScreenTarget *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdDefineGBScreenTarget(pThis, pThisCC, pCmd);
         break;
     }
@@ -1214,5 +2253,5 @@
         SVGA3dCmdDestroyGBScreenTarget *pCmd = (SVGA3dCmdDestroyGBScreenTarget *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdDestroyGBScreenTarget(pThis, pThisCC, pCmd);
         break;
     }
@@ -1222,5 +2261,5 @@
         SVGA3dCmdBindGBScreenTarget *pCmd = (SVGA3dCmdBindGBScreenTarget *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdBindGBScreenTarget(pThisCC, pCmd);
         break;
     }
@@ -1230,5 +2269,5 @@
         SVGA3dCmdUpdateGBScreenTarget *pCmd = (SVGA3dCmdUpdateGBScreenTarget *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdUpdateGBScreenTarget(pThisCC, pCmd);
         break;
     }
@@ -1295,5 +2334,5 @@
         SVGA3dCmdDefineGBMob64 *pCmd = (SVGA3dCmdDefineGBMob64 *)pvCmd;
         VMSVGAFIFO_CHECK_3D_CMD_MIN_SIZE_BREAK(sizeof(*pCmd));
-        VMSVGA_3D_CMD_NOTIMPL(); RT_NOREF(pCmd);
+        vmsvga3dCmdDefineGBMob64(pThisCC, pCmd);
         break;
     }
@@ -2713,2 +3752,65 @@
 }
 
+
+/**
+ * Clip.
+ *
+ * @param   pBound  Bounding rectangle.
+ * @param   pRect   Rectangle to be clipped.
+ */
+void vmsvgaR3Clip3dRect(SVGA3dRect const *pBound, SVGA3dRect RT_UNTRUSTED_GUEST *pRect)
+{
+    uint32_t const leftBound   = pBound->x;
+    uint32_t const rightBound  = pBound->x + pBound->w;
+    uint32_t const topBound    = pBound->y;
+    uint32_t const bottomBound = pBound->y + pBound->h;
+
+    uint32_t x = pRect->x;
+    uint32_t y = pRect->y;
+    uint32_t w = pRect->w;
+    uint32_t h = pRect->h;
+
+    /* Make sure that right and bottom coordinates can be safely computed. */
+    if (x > rightBound)
+        x = rightBound;
+    if (w > rightBound - x)
+        w = rightBound - x;
+    if (y > bottomBound)
+        y = bottomBound;
+    if (h > bottomBound - y)
+        h = bottomBound - y;
+
+    /* Switch from x, y, w, h to left, top, right, bottom. */
+    uint32_t left   = x;
+    uint32_t right  = x + w;
+    uint32_t top    = y;
+    uint32_t bottom = y + h;
+
+    /* A standard left, right, bottom, top clipping. */
+    if (left < leftBound)
+        left = leftBound;
+    if (right < leftBound)
+        right = leftBound;
+
+    if (left > rightBound)
+        left = rightBound;
+    if (right > rightBound)
+        right = rightBound;
+
+    if (top < topBound)
+        top = topBound;
+    if (bottom < topBound)
+        bottom = topBound;
+
+    if (top > bottomBound)
+        top = bottomBound;
+    if (bottom > bottomBound)
+        bottom = bottomBound;
+
+    /* Back to x, y, w, h representation. */
+    pRect->x = left;
+    pRect->y = top;
+    pRect->w = right - left;
+    pRect->h = bottom - top;
+}
+
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h	(revision 86838)
@@ -29,4 +29,7 @@
 #endif
 
+#include <iprt/avl.h>
+#include <iprt/list.h>
+
 
 /*********************************************************************************************************************************
@@ -47,9 +50,52 @@
 typedef struct
 {
-    uint32_t                    cMaxPages;
-    uint32_t                    cbTotal;
-    uint32_t                    numDescriptors;
-    PVMSVGAGMRDESCRIPTOR        paDesc;
+    uint32_t                cMaxPages;
+    uint32_t                cbTotal;
+    uint32_t                numDescriptors;
+    PVMSVGAGMRDESCRIPTOR    paDesc;
 } GMR, *PGMR;
+
+/*
+ * GBO (Guest Backed Object).
+ * A GBO is a list of the guest pages. GBOs are used for VMSVGA MOBs (Memory OBjects)
+ * and Object Tables which the guest shares with the host.
+ *
+ * A GBO is similar to a GMR. Nevertheless I'll create a new code for GBOs in order
+ * to avoid tweaking and possibly breaking existing code. Moreover it will be probably possible to
+ * map the guest pages into the host R3 memory and access them directly.
+ */
+
+/* GBO descriptor. */
+typedef struct VMSVGAGBODESCRIPTOR
+{
+   RTGCPHYS                 GCPhys;
+   uint64_t                 cPages;
+} VMSVGAGBODESCRIPTOR, *PVMSVGAGBODESCRIPTOR;
+typedef VMSVGAGBODESCRIPTOR const *PCVMSVGAGBODESCRIPTOR;
+
+/* GBO.
+ */
+typedef struct VMSVGAGBO
+{
+    uint32_t                u32Reserved;
+    uint32_t                cTotalPages;
+    uint32_t                cbTotal;
+    uint32_t                cDescriptors;
+    PVMSVGAGBODESCRIPTOR    paDescriptors;
+} VMSVGAGBO, *PVMSVGAGBO;
+typedef VMSVGAGBO const *PCVMSVGAGBO;
+
+#define VMSVGA_IS_GBO_CREATED(a_Gbo) ((a_Gbo)->paDescriptors != NULL)
+
+/* MOB is also a GBO.
+ */
+typedef struct VMSVGAMOB
+{
+    AVLU32NODECORE          Core; /* Key is the mobid. */
+    RTLISTNODE              nodeLRU;
+    VMSVGAGBO               Gbo;
+} VMSVGAMOB, *PVMSVGAMOB;
+typedef VMSVGAMOB const *PCVMSVGAMOB;
+
 
 typedef struct VMSVGACMDBUF *PVMSVGACMDBUF;
@@ -84,4 +130,5 @@
 typedef struct VMSVGAR3STATE
 {
+    PPDMDEVINS              pDevIns; /* Stored here to use with PDMDevHlp* */
     GMR                    *paGMR; // [VMSVGAState::cGMR]
     struct
@@ -127,4 +174,17 @@
     /** Critical section for accessing the command buffer data. */
     RTCRITSECT              CritSectCmdBuf;
+
+    /**  */
+    VMSVGAGBO               GboOTableMob;
+    VMSVGAGBO               GboOTableSurface;
+    VMSVGAGBO               GboOTableContext;
+    VMSVGAGBO               GboOTableShader;
+    VMSVGAGBO               GboOTableScreenTarget;
+
+    /** Tree of guest's Memory OBjects. Key is mobid. */
+    AVLU32TREE              MOBTree;
+    /** Least Recently Used list of MOBs.
+     * To unmap older MOBs when the guest exceeds SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB (SVGA_REG_GBOBJECT_MEM_SIZE_KB) value. */
+    RTLISTANCHOR            MOBLRUList;
 
     /** Tracks how much time we waste reading SVGA_REG_BUSY with a busy FIFO. */
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp	(revision 86838)
@@ -248,4 +248,5 @@
     SSMFIELD_ENTRY(             VMSVGASCREENOBJECT, fDefined),
     SSMFIELD_ENTRY(             VMSVGASCREENOBJECT, fModified),
+    SSMFIELD_ENTRY_VER(         VMSVGASCREENOBJECT, cDpi, VGA_SAVEDSTATE_VERSION_VMSVGA_MIPLEVELS),
     SSMFIELD_ENTRY_TERM()
 };
@@ -626,5 +627,6 @@
         }
 
-        rc = pThisCC->pDrv->pfnVBVAResize(pThisCC->pDrv, &view, &screen, pThisCC->pbVRam, /*fResetInputMapping=*/ true);
+        void *pvVRAM = pScreen->pvScreenBitmap ? pScreen->pvScreenBitmap : pThisCC->pbVRam;
+        rc = pThisCC->pDrv->pfnVBVAResize(pThisCC->pDrv, &view, &screen, pvVRAM, /*fResetInputMapping=*/ true);
         AssertRC(rc);
     }
@@ -2473,5 +2475,5 @@
 
 /**
- * HC access handler for the FIFO.
+ * HC access handler for GMRs.
  *
  * @returns VINF_SUCCESS if the handler have carried out the operation.
@@ -2631,16 +2633,16 @@
  * @param pDevIns     The device instance.
  * @param pThis       The shared VGA/VMSVGA state.
- * @param fIRQ        SVGA_IRQFLAG_* bits.
+ * @param u32IrqStatus SVGA_IRQFLAG_* bits.
  * @thread FIFO or EMT.
  */
-static void vmsvgaR3CmdBufRaiseIRQ(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t fIRQ)
+static void vmsvgaR3CmdBufRaiseIRQ(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t u32IrqStatus)
 {
     int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
     AssertRC(rc);
 
-    if (pThis->svga.u32IrqMask & fIRQ)
-    {
-        LogFunc(("Trigger interrupt with status %#x\n", fIRQ));
-        ASMAtomicOrU32(&pThis->svga.u32IrqStatus, fIRQ);
+    if (pThis->svga.u32IrqMask & u32IrqStatus)
+    {
+        LogFunc(("Trigger interrupt with status %#x\n", u32IrqStatus));
+        ASMAtomicOrU32(&pThis->svga.u32IrqStatus, u32IrqStatus);
         PDMDevHlpPCISetIrq(pDevIns, 0, 1);
     }
@@ -3086,8 +3088,9 @@
  * @param cbCommands   Size of the command buffer.
  * @param poffNextCmd  Where to store the offset of the first unprocessed command.
+ * @param pu32IrqStatus Where to store SVGA_IRQFLAG_ if the IRQ is generated by the last command in the buffer.
  * @return SVGACBStatus code.
  * @thread FIFO
  */
-static SVGACBStatus vmsvgaR3CmdBufProcessCommands(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, void const *pvCommands, uint32_t cbCommands, uint32_t *poffNextCmd)
+static SVGACBStatus vmsvgaR3CmdBufProcessCommands(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, void const *pvCommands, uint32_t cbCommands, uint32_t *poffNextCmd, uint32_t *pu32IrqStatus)
 {
     SVGACBStatus CBstatus = SVGA_CB_STATUS_COMPLETED;
@@ -3144,9 +3147,8 @@
                     pFIFO[SVGA_FIFO_FENCE] = pCmd->fence;
 
-                    uint32_t u32IrqStatus = 0;
                     if (pThis->svga.u32IrqMask & SVGA_IRQFLAG_ANY_FENCE)
                     {
                         Log(("any fence irq\n"));
-                        u32IrqStatus |= SVGA_IRQFLAG_ANY_FENCE;
+                        *pu32IrqStatus |= SVGA_IRQFLAG_ANY_FENCE;
                     }
                     else if (    VMSVGA_IS_VALID_FIFO_REG(SVGA_FIFO_FENCE_GOAL, offFifoMin)
@@ -3155,9 +3157,6 @@
                     {
                         Log(("fence goal reached irq (fence=%#x)\n", pCmd->fence));
-                        u32IrqStatus |= SVGA_IRQFLAG_FENCE_GOAL;
+                        *pu32IrqStatus |= SVGA_IRQFLAG_FENCE_GOAL;
                     }
-
-                    if (u32IrqStatus)
-                        vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, u32IrqStatus);
                 }
                 else
@@ -3321,5 +3320,5 @@
                 break;
             }
-# endif // VBOX_WITH_VMSVGA3D
+# endif /* VBOX_WITH_VMSVGA3D */
             case SVGA_CMD_DEFINE_SCREEN:
             {
@@ -3432,4 +3431,16 @@
         pu8Cmd += cbCmd;
         cbRemain -= cbCmd;
+
+        /* If this is not the last command in the buffer, then generate IRQ, if required.
+         * This avoids a double call to vmsvgaR3CmdBufRaiseIRQ if FENCE is the last command
+         * in the buffer (usually the case).
+         */
+        if (RT_LIKELY(!(cbRemain && *pu32IrqStatus)))
+        { /* likely */ }
+        else
+        {
+            vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, *pu32IrqStatus);
+            *pu32IrqStatus = 0;
+        }
     }
 
@@ -3492,17 +3503,17 @@
         SVGACBStatus CBstatus = SVGA_CB_STATUS_NONE;
         uint32_t offNextCmd = 0;
+        uint32_t u32IrqStatus = 0;
 
         /* Process one buffer. */
-        CBstatus = vmsvgaR3CmdBufProcessCommands(pDevIns, pThis, pThisCC, pCmdBuf->pvCommands, pCmdBuf->hdr.length, &offNextCmd);
-
-        uint32_t fIRQ = 0;
+        CBstatus = vmsvgaR3CmdBufProcessCommands(pDevIns, pThis, pThisCC, pCmdBuf->pvCommands, pCmdBuf->hdr.length, &offNextCmd, &u32IrqStatus);
+
         if (!RT_BOOL(pCmdBuf->hdr.flags & SVGA_CB_FLAG_NO_IRQ))
-            fIRQ |= SVGA_IRQFLAG_COMMAND_BUFFER;
+            u32IrqStatus |= SVGA_IRQFLAG_COMMAND_BUFFER;
         if (CBstatus == SVGA_CB_STATUS_COMMAND_ERROR)
-            fIRQ |= SVGA_IRQFLAG_ERROR;
+            u32IrqStatus |= SVGA_IRQFLAG_ERROR;
 
         vmsvgaR3CmdBufWriteStatus(pDevIns, pCmdBuf->GCPhysCB, CBstatus, offNextCmd);
-        if (fIRQ)
-            vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, fIRQ);
+        if (u32IrqStatus)
+            vmsvgaR3CmdBufRaiseIRQ(pDevIns, pThis, u32IrqStatus);
 
         vmsvgaR3CmdBufFree(pCmdBuf);
@@ -5380,11 +5391,13 @@
  *
  * @returns VBox status code.
+ * @param   pDevIns        The PDM device instance.
  * @param   pThis          The shared VGA/VMSVGA instance data.
  * @param   pSVGAState     Pointer to the structure. It is already allocated.
  */
-static int vmsvgaR3StateInit(PVGASTATE pThis, PVMSVGAR3STATE pSVGAState)
+static int vmsvgaR3StateInit(PPDMDEVINS pDevIns, PVGASTATE pThis, PVMSVGAR3STATE pSVGAState)
 {
     int rc = VINF_SUCCESS;
     RT_ZERO(*pSVGAState);
+    pSVGAState->pDevIns = pDevIns;
 
     pSVGAState->paGMR = (PGMR)RTMemAllocZ(pThis->svga.cGMR * sizeof(GMR));
@@ -5401,4 +5414,6 @@
 
     vmsvgaR3CmdBufCtxInit(&pSVGAState->CmdBufCtxDC);
+
+    RTListInit(&pSVGAState->MOBLRUList);
     return rc;
 }
@@ -5808,5 +5823,5 @@
 
     vmsvgaR3StateTerm(pThis, pThisCC->svga.pSvgaR3State);
-    vmsvgaR3StateInit(pThis, pThisCC->svga.pSvgaR3State);
+    vmsvgaR3StateInit(pDevIns, pThis, pThisCC->svga.pSvgaR3State);
 
     RT_BZERO(pThisCC->svga.pbVgaFrameBufferR3, VMSVGA_VGA_FB_BACKUP_SIZE);
@@ -5925,5 +5940,5 @@
     AssertReturn(pThisCC->svga.pSvgaR3State, VERR_NO_MEMORY);
 
-    rc = vmsvgaR3StateInit(pThis, pThisCC->svga.pSvgaR3State);
+    rc = vmsvgaR3StateInit(pDevIns, pThis, pThisCC->svga.pSvgaR3State);
     AssertMsgRCReturn(rc, ("Failed to create pSvgaR3State.\n"), rc);
 
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA.h
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA.h	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA.h	(revision 86838)
@@ -254,6 +254,9 @@
     /** Bits per pixel. */
     uint32_t    cBpp;
+    /** The physical DPI that the guest expects for this screen. Zero, if the guest is not DPI aware. */
+    uint32_t    cDpi;
     bool        fDefined;
     bool        fModified;
+    void       *pvScreenBitmap;
 #ifdef VBOX_WITH_VMSVGA3D
     /** Pointer to the HW accelerated (3D) screen data. */
@@ -361,5 +364,5 @@
     uint32_t                    uPadding1;
 #endif
-    /** Number of GMRs. */
+    /** Number of GMRs (VMSVGA_MAX_GMR_IDS, count of elements in VMSVGAR3STATE::paGMR array). */
     uint32_t                    cGMR;
     uint32_t                    uScreenOffset; /* Used only for loading older saved states. */
@@ -566,4 +569,5 @@
 void vmsvgaR3ClipBox(const SVGA3dSize *pSize, SVGA3dBox *pBox);
 void vmsvgaR3ClipRect(SVGASignedRect const *pBound, SVGASignedRect *pRect);
+void vmsvgaR3Clip3dRect(SVGA3dRect const *pBound, SVGA3dRect RT_UNTRUSTED_GUEST *pRect);
 
 #endif /* !VBOX_INCLUDED_SRC_Graphics_DevVGA_SVGA_h */
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-info.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-info.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-info.cpp	(revision 86838)
@@ -291,7 +291,6 @@
         for (uint32_t iFace = 0; iFace < pSurface->cFaces; iFace++)
         {
-            Assert(pSurface->faces[iFace].numMipLevels <= pSurface->faces[0].numMipLevels);
-            PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[iFace * pSurface->faces[0].numMipLevels];
-            for (uint32_t i = 0; i < pSurface->faces[iFace].numMipLevels; i++, pMipmapLevel++)
+            PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[iFace * pSurface->cLevels];
+            for (uint32_t i = 0; i < pSurface->cLevels; i++, pMipmapLevel++)
             {
                 if (VMSVGA3DSURFACE_HAS_HW_SURFACE(pSurface))
@@ -498,4 +497,7 @@
                             AssertMsgFailed(("%#x\n", fSwitchFlags));
                     }
+#elif defined(VMSVGA3D_DX)
+                    /** @todo */
+                    RT_NOREF(pState);
 #else
 # error "misconfigured"
@@ -1522,4 +1524,6 @@
 # ifdef VMSVGA3D_DIRECT3D
     pHlp->pfnPrintf(pHlp, "pDevice:                 %p\n", pContext->pDevice);
+# elif defined(VMSVGA3D_DX)
+    /** @todo */
 # else
     pHlp->pfnPrintf(pHlp, "hdc:                     %p\n", pContext->hdc);
@@ -1919,5 +1923,5 @@
     const uint32_t u32Seq = ASMAtomicIncU32(&sSeq);
 
-    for (uint32_t i = 0; i < pSurface->faces[0].numMipLevels; ++i)
+    for (uint32_t i = 0; i < pSurface->cLevels; ++i)
     {
         if (!pSurface->paMipmapLevels[i].pSurfaceData)
@@ -1978,14 +1982,12 @@
     vmsvga3dInfoU32Flags(pHlp, pSurface->surfaceFlags, "SVGA3D_SURFACE_", g_aSvga3DSurfaceFlags, RT_ELEMENTS(g_aSvga3DSurfaceFlags));
     pHlp->pfnPrintf(pHlp, "\n");
-    if (pSurface->cFaces == 0)
+    if (pSurface->cFaces != 0)
         pHlp->pfnPrintf(pHlp, "Faces:                   %u\n", pSurface->cFaces);
+    if (pSurface->cLevels != 0)
+        pHlp->pfnPrintf(pHlp, "Mipmap levels:  %u\n", pSurface->cLevels);
     for (uint32_t iFace = 0; iFace < pSurface->cFaces; iFace++)
     {
-        Assert(pSurface->faces[iFace].numMipLevels <= pSurface->faces[0].numMipLevels);
-        if (pSurface->faces[iFace].numMipLevels == 0)
-            pHlp->pfnPrintf(pHlp, "Faces[%u] Mipmap levels:  %u\n", iFace, pSurface->faces[iFace].numMipLevels);
-
-        uint32_t iMipmap = iFace * pSurface->faces[0].numMipLevels;
-        for (uint32_t iLevel = 0; iLevel < pSurface->faces[iFace].numMipLevels; iLevel++, iMipmap++)
+        uint32_t iMipmap = iFace * pSurface->cLevels;
+        for (uint32_t iLevel = 0; iLevel < pSurface->cLevels; iLevel++, iMipmap++)
         {
             pHlp->pfnPrintf(pHlp, "Face #%u, mipmap #%u[%u]:%s  cx=%u, cy=%u, cz=%u, cbSurface=%#x, cbPitch=%#x",
@@ -2030,4 +2032,6 @@
     pHlp->pfnPrintf(pHlp, "fStencilAsTexture:       %RTbool\n", pSurface->fStencilAsTexture);
 
+#elif defined(VMSVGA3D_DX)
+    /** @todo */
 #elif defined(VMSVGA3D_OPENGL)
     /** @todo   */
@@ -2039,6 +2043,6 @@
         for (uint32_t iFace = 0; iFace < pSurface->cFaces; iFace++)
         {
-            uint32_t iMipmap = iFace * pSurface->faces[0].numMipLevels;
-            for (uint32_t iLevel = 0; iLevel < pSurface->faces[iFace].numMipLevels; iLevel++, iMipmap++)
+            uint32_t iMipmap = iFace * pSurface->cLevels;
+            for (uint32_t iLevel = 0; iLevel < pSurface->cLevels; iLevel++, iMipmap++)
                 if (pSurface->paMipmapLevels[iMipmap].pSurfaceData)
                 {
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-internal.h
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-internal.h	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-internal.h	(revision 86838)
@@ -29,8 +29,8 @@
 #endif
 #ifdef VMSVGA3D_OPENGL
-# ifdef VMSVGA3D_DIRECT3D
+# if defined(VMSVGA3D_DIRECT3D) || defined(VMSVGA3D_DX)
 #  error "Both VMSVGA3D_DIRECT3D and VMSVGA3D_OPENGL cannot be defined at the same time."
 # endif
-#elif !defined(VMSVGA3D_DIRECT3D)
+#elif !defined(VMSVGA3D_DIRECT3D) && !defined(VMSVGA3D_DX)
 # error "Either VMSVGA3D_OPENGL or VMSVGA3D_DIRECT3D must be defined."
 #endif
@@ -51,4 +51,6 @@
 #  include <d3d9.h>
 #  include <iprt/avl.h>
+# elif defined(VMSVGA3D_DX)
+#  include <d3d11.h>
 # else
 #  include <GL/gl.h>
@@ -536,4 +538,7 @@
 #endif
 
+/* The 3D backend surface. The actual structure is 3D API specific. */
+typedef struct VMSVGA3DBACKENDSURFACE *PVMSVGA3DBACKENDSURFACE;
+
 /**
  * VMSVGA3d surface.
@@ -541,4 +546,6 @@
 typedef struct VMSVGA3DSURFACE
 {
+    PVMSVGA3DBACKENDSURFACE pBackendSurface;
+
     uint32_t                id;
 #ifdef VMSVGA3D_OPENGL
@@ -566,8 +573,7 @@
     GLuint                  idEmulated; /* GL name of the intermediate texture. */
 #endif
-    SVGA3dSurfaceFace       faces[SVGA3D_MAX_SURFACE_FACES];
-    uint32_t                cFaces;
-    uint32_t                cMipmapLevels;
-    PVMSVGA3DMIPMAPLEVEL    paMipmapLevels;
+    uint32_t                cFaces; /* Number of faces: 6 for cubemaps, 1 for everything else. */
+    uint32_t                cLevels; /* Number of mipmap levels per face. */
+    PVMSVGA3DMIPMAPLEVEL    paMipmapLevels; /* cFaces * cLevels elements. */
     uint32_t                multiSampleCount;
     SVGA3dTextureFilter     autogenFilter;
@@ -646,27 +652,10 @@
     SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, formatGL),
     SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, typeGL),
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, id),
 # endif
-    SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, faces),
     SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, cFaces),
-    SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DSURFACE, paMipmapLevels),
+    SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, cLevels),
     SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, multiSampleCount),
     SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, autogenFilter),
-# ifdef VMSVGA3D_DIRECT3D
-    SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, format), /** @todo format duplicated. */
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, formatD3D),
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, fUsageD3D),
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, multiSampleTypeD3D),
-# endif
     SSMFIELD_ENTRY(                 VMSVGA3DSURFACE, cbBlock),
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, fDirty),
-# ifdef VMSVGA3D_DIRECT3D
-    SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DSURFACE, hSharedObject),
-    SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DSURFACE, pQuery),
-    SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DSURFACE, u.pSurface),
-    SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DSURFACE, bounce.pTexture),
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, pSharedObjectTree),
-    SSMFIELD_ENTRY_IGNORE(          VMSVGA3DSURFACE, fStencilAsTexture),
-# endif
     SSMFIELD_ENTRY_TERM()
 };
@@ -687,4 +676,6 @@
 #ifdef VMSVGA3D_DIRECT3D
 # define VMSVGA3DSURFACE_HAS_HW_SURFACE(a_pSurface) ((a_pSurface)->u.pSurface != NULL)
+#elif defined(VMSVGA3D_DX)
+# define VMSVGA3DSURFACE_HAS_HW_SURFACE(a_pSurface) ((a_pSurface)->pBackendSurface != NULL)
 #else
 # define VMSVGA3DSURFACE_HAS_HW_SURFACE(a_pSurface) ((a_pSurface)->oglId.texture != OPENGL_INVALID_ID)
@@ -701,4 +692,7 @@
    (   (a_pSurface)->enmD3DResType == VMSVGA3D_D3DRESTYPE_VERTEX_BUFFER \
     || (a_pSurface)->enmD3DResType == VMSVGA3D_D3DRESTYPE_INDEX_BUFFER)
+#elif defined(VMSVGA3D_DX)
+  /** @todo */
+# define VMSVGA3DSURFACE_NEEDS_DATA(a_pSurface) (false)
 #else
 # define VMSVGA3DSURFACE_NEEDS_DATA(a_pSurface) \
@@ -719,4 +713,6 @@
         IDirect3DVertexShader9     *pVertexShader;
         IDirect3DPixelShader9      *pPixelShader;
+#elif defined(VMSVGA3D_DX)
+        /** todo */
 #else
         void                       *pVertexShader;
@@ -770,4 +766,6 @@
 #ifdef VMSVGA3D_DIRECT3D
     IDirect3DQuery9    *pQuery;
+#elif defined(VMSVGA3D_DX)
+    /** @todo */
 #else /* VMSVGA3D_OPENGL */
     GLuint              idQuery;
@@ -785,4 +783,6 @@
 #ifdef VMSVGA3D_DIRECT3D
     SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DQUERY, pQuery),
+#elif defined(VMSVGA3D_DX)
+    /** @todo */
 #else /* VMSVGA3D_OPENGL */
     SSMFIELD_ENTRY_IGNORE(          VMSVGA3DQUERY, idQuery),
@@ -796,4 +796,7 @@
 #ifdef VMSVGA3D_DIRECT3D
 #define VMSVGA3DQUERY_EXISTS(p) ((p)->pQuery && (p)->enmQueryState != VMSVGA3DQUERYSTATE_NULL)
+#elif defined(VMSVGA3D_DX)
+    /** @todo */
+#define VMSVGA3DQUERY_EXISTS(p) (false)
 #else
 #define VMSVGA3DQUERY_EXISTS(p) ((p)->idQuery && (p)->enmQueryState != VMSVGA3DQUERYSTATE_NULL)
@@ -813,4 +816,6 @@
     IDirect3DDevice9Ex     *pDevice;
 #  endif
+# elif defined(VMSVGA3D_DX)
+    /** @todo */
 # else
     /* Device context of the context window. */
@@ -911,4 +916,6 @@
 #  ifdef VMSVGA3D_DIRECT3D
     SSMFIELD_ENTRY_IGN_HCPTR(       VMSVGA3DCONTEXT, pDevice),
+#  elif defined(VMSVGA3D_DX)
+    /** @todo */
 #  else
     SSMFIELD_ENTRY_IGNORE(          VMSVGA3DCONTEXT, hdc),
@@ -967,4 +974,7 @@
 #endif
 
+/* The 3D backend. The actual structure is 3D API specific. */
+typedef struct VMSVGA3DBACKEND *PVMSVGA3DBACKEND;
+
 /**
  * VMSVGA3d state data.
@@ -996,4 +1006,6 @@
     bool                    fSupportedFormatYUY2 : 1;
     bool                    fSupportedFormatA8B8G8R8 : 1;
+# elif defined(VMSVGA3D_DX)
+    PVMSVGA3DBACKEND        pBackend;
 # endif
     /** Window Thread. */
@@ -1297,15 +1309,12 @@
                                     PVMSVGA3DMIPMAPLEVEL *ppMipmapLevel)
 {
-    /* Can use faces[0].numMipLevels, because numMipLevels is the same for all faces. */
-    const uint32_t numMipLevels = pSurface->faces[0].numMipLevels;
-
     AssertMsgReturn(face < pSurface->cFaces,
                     ("cFaces %d, face %d\n", pSurface->cFaces, face),
                     VERR_INVALID_PARAMETER);
-    AssertMsgReturn(mipmap < numMipLevels,
-                    ("numMipLevels %d, mipmap %d", numMipLevels, mipmap),
+    AssertMsgReturn(mipmap < pSurface->cLevels,
+                    ("numMipLevels %d, mipmap %d", pSurface->cLevels, mipmap),
                     VERR_INVALID_PARAMETER);
 
-    *ppMipmapLevel = &pSurface->paMipmapLevels[face * numMipLevels + mipmap];
+    *ppMipmapLevel = &pSurface->paMipmapLevels[face * pSurface->cLevels + mipmap];
     return VINF_SUCCESS;
 }
@@ -1327,4 +1336,20 @@
     return Face;
 }
+#elif defined(VMSVGA3D_DX)
+DECLINLINE(D3D11_TEXTURECUBE_FACE) vmsvga3dCubemapFaceFromIndex(uint32_t iFace)
+{
+    D3D11_TEXTURECUBE_FACE Face;
+    switch (iFace)
+    {
+        case 0: Face = D3D11_TEXTURECUBE_FACE_POSITIVE_X; break;
+        case 1: Face = D3D11_TEXTURECUBE_FACE_NEGATIVE_X; break;
+        case 2: Face = D3D11_TEXTURECUBE_FACE_POSITIVE_Y; break;
+        case 3: Face = D3D11_TEXTURECUBE_FACE_NEGATIVE_Y; break;
+        case 4: Face = D3D11_TEXTURECUBE_FACE_POSITIVE_Z; break;
+        default:
+        case 5: Face = D3D11_TEXTURECUBE_FACE_NEGATIVE_Z; break;
+    }
+    return Face;
+}
 #else /* VMSVGA3D_OPENGL */
 DECLINLINE(GLenum) vmsvga3dCubemapFaceFromIndex(uint32_t iFace)
@@ -1354,5 +1379,5 @@
                                  const char *pszPath, const char *pszNamePrefix, const char *pszNameSuffix);
 
-#ifdef VMSVGA3D_DIRECT3D
+#if defined(VMSVGA3D_DIRECT3D) || defined(VMSVGA3D_DX)
 #define D3D_RELEASE(ptr) do { \
     if (ptr)                  \
@@ -1362,5 +1387,7 @@
     }                         \
 } while (0)
-
+#endif
+
+#if defined(VMSVGA3D_DIRECT3D)
 HRESULT D3D9UpdateTexture(PVMSVGA3DCONTEXT pContext,
                           PVMSVGA3DSURFACE pSurface);
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-ogl.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-ogl.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-ogl.cpp	(revision 86838)
@@ -2366,5 +2366,5 @@
     LogFunc(("sid=%u\n", pSurface->id));
 
-    uint32_t const numMipLevels = pSurface->faces[0].numMipLevels;
+    uint32_t const numMipLevels = pSurface->cLevels;
 
     /* Fugure out what kind of texture we are creating. */
@@ -2425,5 +2425,5 @@
     glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
     VMSVGA3D_CHECK_LAST_ERROR(pState, pContext);
-    glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, pSurface->faces[0].numMipLevels - 1);
+    glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, pSurface->cLevels - 1);
     VMSVGA3D_CHECK_LAST_ERROR(pState, pContext);
 
@@ -5722,5 +5722,5 @@
                     {
                         Log(("CreateTexture (%d,%d) levels=%d\n",
-                              pSurface->paMipmapLevels[0].mipmapSize.width, pSurface->paMipmapLevels[0].mipmapSize.height, pSurface->faces[0].numMipLevels));
+                              pSurface->paMipmapLevels[0].mipmapSize.width, pSurface->paMipmapLevels[0].mipmapSize.height, pSurface->cLevels));
                         rc = vmsvga3dBackCreateTexture(pState, pContext, cid, pSurface);
                         AssertRCReturn(rc, rc);
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-savedstate.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-savedstate.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-savedstate.cpp	(revision 86838)
@@ -147,4 +147,80 @@
 }
 
+static int vmsvga3dLoadVMSVGA3DSURFACEPreMipLevels(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, VMSVGA3DSURFACE *pSurface)
+{
+    typedef struct VMSVGA3DSURFACEPreMipLevels
+    {
+        uint32_t            id;
+#ifdef VMSVGA3D_OPENGL
+        uint32_t            idWeakContextAssociation;
+#else
+        uint32_t            idAssociatedContext;
+#endif
+        uint32_t            surfaceFlags;
+        SVGA3dSurfaceFormat format;
+#ifdef VMSVGA3D_OPENGL
+        GLint               internalFormatGL;
+        GLint               formatGL;
+        GLint               typeGL;
+#endif
+        SVGA3dSurfaceFace   faces[SVGA3D_MAX_SURFACE_FACES];
+        uint32_t            cFaces;
+        uint32_t            multiSampleCount;
+        SVGA3dTextureFilter autogenFilter;
+        uint32_t            cbBlock;
+    } VMSVGA3DSURFACEPreMipLevels;
+
+    static SSMFIELD const s_aVMSVGA3DSURFACEFieldsPreMipLevels[] =
+    {
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, id),
+#ifdef VMSVGA3D_OPENGL
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, idWeakContextAssociation),
+#else
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, idAssociatedContext),
+#endif
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, surfaceFlags),
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, format),
+#ifdef VMSVGA3D_OPENGL
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, internalFormatGL),
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, formatGL),
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, typeGL),
+#endif
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, faces),
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, cFaces),
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, multiSampleCount),
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, autogenFilter),
+#ifdef VMSVGA3D_DIRECT3D
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, format), /* Yes, the 'format' field is duplicated. */
+#endif
+        SSMFIELD_ENTRY(VMSVGA3DSURFACEPreMipLevels, cbBlock),
+        SSMFIELD_ENTRY_TERM()
+    };
+
+    VMSVGA3DSURFACEPreMipLevels surfacePreMipLevels;
+    int rc = pDevIns->pHlpR3->pfnSSMGetStructEx(pSSM, &surfacePreMipLevels, sizeof(surfacePreMipLevels), 0, s_aVMSVGA3DSURFACEFieldsPreMipLevels, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        pSurface->id                       = surfacePreMipLevels.id;
+#ifdef VMSVGA3D_OPENGL
+        pSurface->idWeakContextAssociation = surfacePreMipLevels.idWeakContextAssociation;
+#else
+        pSurface->idAssociatedContext      = surfacePreMipLevels.idAssociatedContext;
+#endif
+        pSurface->surfaceFlags             = surfacePreMipLevels.surfaceFlags;
+        pSurface->format                   = surfacePreMipLevels.format;
+#ifdef VMSVGA3D_OPENGL
+        pSurface->internalFormatGL         = surfacePreMipLevels.internalFormatGL;
+        pSurface->formatGL                 = surfacePreMipLevels.formatGL;
+        pSurface->typeGL                   = surfacePreMipLevels.typeGL;
+#endif
+        pSurface->cLevels                  = surfacePreMipLevels.faces[0].numMipLevels;
+        pSurface->cFaces                   = surfacePreMipLevels.cFaces;
+        pSurface->multiSampleCount         = surfacePreMipLevels.multiSampleCount;
+        pSurface->autogenFilter            = surfacePreMipLevels.autogenFilter;
+        pSurface->cbBlock                  = surfacePreMipLevels.cbBlock;
+    }
+    return rc;
+}
+
 int vmsvga3dLoadExec(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
 {
@@ -408,9 +484,12 @@
 
             /* Fetch the surface structure first. */
-            rc = pHlp->pfnSSMGetStructEx(pSSM, &surface, sizeof(surface), 0, g_aVMSVGA3DSURFACEFields, NULL);
+            if (RT_LIKELY(uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_MIPLEVELS))
+                rc = pHlp->pfnSSMGetStructEx(pSSM, &surface, sizeof(surface), 0, g_aVMSVGA3DSURFACEFields, NULL);
+            else
+                rc = vmsvga3dLoadVMSVGA3DSURFACEPreMipLevels(pDevIns, pSSM, &surface);
             AssertRCReturn(rc, rc);
 
             {
-                uint32_t             cMipLevels = surface.faces[0].numMipLevels * surface.cFaces;
+                uint32_t             cMipLevels = surface.cLevels * surface.cFaces;
                 PVMSVGA3DMIPMAPLEVEL pMipmapLevel = (PVMSVGA3DMIPMAPLEVEL)RTMemAlloc(cMipLevels * sizeof(VMSVGA3DMIPMAPLEVEL));
                 AssertReturn(pMipmapLevel, VERR_NO_MEMORY);
@@ -421,7 +500,7 @@
                 for (uint32_t face=0; face < surface.cFaces; face++)
                 {
-                    for (uint32_t j = 0; j < surface.faces[0].numMipLevels; j++)
+                    for (uint32_t j = 0; j < surface.cLevels; j++)
                     {
-                        uint32_t idx = j + face * surface.faces[0].numMipLevels;
+                        uint32_t idx = j + face * surface.cLevels;
                         /* Load the mip map level struct. */
                         rc = pHlp->pfnSSMGetStructEx(pSSM, &pMipmapLevel[idx], sizeof(pMipmapLevel[idx]), 0,
@@ -433,6 +512,6 @@
                 }
 
-                rc = vmsvga3dSurfaceDefine(pThisCC, sid, surface.surfaceFlags, surface.format, surface.faces,
-                                           surface.multiSampleCount, surface.autogenFilter, cMipLevels, pMipmapLevelSize);
+                rc = vmsvga3dSurfaceDefine(pThisCC, sid, surface.surfaceFlags, surface.format, surface.multiSampleCount,
+                                           surface.autogenFilter, surface.cLevels, &pMipmapLevelSize[0]);
                 AssertRCReturn(rc, rc);
 
@@ -447,5 +526,5 @@
 
             /* Load the mip map level data. */
-            for (uint32_t j = 0; j < pSurface->faces[0].numMipLevels * pSurface->cFaces; j++)
+            for (uint32_t j = 0; j < pSurface->cLevels * pSurface->cFaces; j++)
             {
                 PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[j];
@@ -684,7 +763,7 @@
             for (uint32_t face=0; face < pSurface->cFaces; face++)
             {
-                for (uint32_t i = 0; i < pSurface->faces[0].numMipLevels; i++)
+                for (uint32_t i = 0; i < pSurface->cLevels; i++)
                 {
-                    uint32_t idx = i + face * pSurface->faces[0].numMipLevels;
+                    uint32_t idx = i + face * pSurface->cLevels;
                     PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[idx];
 
@@ -698,7 +777,7 @@
             for (uint32_t face=0; face < pSurface->cFaces; face++)
             {
-                for (uint32_t i = 0; i < pSurface->faces[0].numMipLevels; i++)
+                for (uint32_t i = 0; i < pSurface->cLevels; i++)
                 {
-                    uint32_t idx = i + face * pSurface->faces[0].numMipLevels;
+                    uint32_t idx = i + face * pSurface->cLevels;
                     PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[idx];
 
@@ -877,4 +956,6 @@
 
                         RTMemFree(pData);
+#elif defined(VMSVGA3D_DX)
+                        /** @todo */
 #elif defined(VMSVGA3D_OPENGL)
                         void *pData = NULL;
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-win.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-win.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d-win.cpp	(revision 86838)
@@ -1196,5 +1196,5 @@
         const uint32_t cHeight = pSurface->paMipmapLevels[0].mipmapSize.height;
         const uint32_t cDepth = pSurface->paMipmapLevels[0].mipmapSize.depth;
-        const uint32_t numMipLevels = pSurface->faces[0].numMipLevels;
+        const uint32_t numMipLevels = pSurface->cLevels;
 
         LogFunc(("Create shared %stexture copy d3d (%d,%d,%d) cMip=%d usage %x format %x.\n",
@@ -1853,5 +1853,5 @@
     const uint32_t cHeight = pSurface->paMipmapLevels[0].mipmapSize.height;
     const uint32_t cDepth = pSurface->paMipmapLevels[0].mipmapSize.depth;
-    const uint32_t numMipLevels = pSurface->faces[0].numMipLevels;
+    const uint32_t numMipLevels = pSurface->cLevels;
 
     /*
@@ -1908,5 +1908,5 @@
     {
         Assert(pSurface->cFaces == 1);
-        Assert(pSurface->faces[0].numMipLevels == 1);
+        Assert(pSurface->cLevels == 1);
         Assert(cDepth == 1);
 
@@ -2223,5 +2223,5 @@
 
         /* We will now use the bounce texture for all memory accesses, so free our surface memory buffer. */
-        for (uint32_t i = 0; i < pSurface->faces[0].numMipLevels; i++)
+        for (uint32_t i = 0; i < pSurface->cLevels; i++)
         {
             RTMemFree(pSurface->paMipmapLevels[i].pSurfaceData);
@@ -2709,5 +2709,5 @@
                 SVGA3dTextureFilter const   autogenFilter    = pSurface->autogenFilter;
                 uint32_t const              cFaces           = pSurface->cFaces;
-                uint32_t const              cMipLevels       = pSurface->faces[0].numMipLevels;
+                uint32_t const              cMipLevels       = pSurface->cLevels;
 
                 SVGA3dSize *pMipLevelSize = (SVGA3dSize *)RTMemAllocZ(cMipLevels * cFaces * sizeof(SVGA3dSize));
@@ -2723,8 +2723,4 @@
                 }
 
-                SVGA3dSurfaceFace aFaces[SVGA3D_MAX_SURFACE_FACES];
-                AssertCompile(sizeof(aFaces) == sizeof(pSurface->faces));
-                memcpy(aFaces, pSurface->faces, sizeof(pSurface->faces));
-
                 /* Recreate the surface with the original settings; destroys the contents, but that seems fairly safe since the context is also destroyed. */
 #ifdef DEBUG_sunlover
@@ -2736,6 +2732,6 @@
                 AssertRC(rc);
 
-                rc = vmsvga3dSurfaceDefine(pThisCC, sid, surfaceFlags, format, aFaces, multisampleCount, autogenFilter,
-                                           cMipLevels * cFaces, pMipLevelSize);
+                rc = vmsvga3dSurfaceDefine(pThisCC, sid, surfaceFlags, format, multisampleCount, autogenFilter,
+                                           cMipLevels, &pMipLevelSize[0]);
                 AssertRC(rc);
 
@@ -4059,7 +4055,7 @@
             {
                 Log(("vmsvga3dSetRenderTarget: sync dirty depth/stencil buffer\n"));
-                Assert(pRenderTarget->faces[0].numMipLevels == 1);
-
-                for (uint32_t i = 0; i < pRenderTarget->faces[0].numMipLevels; i++)
+                Assert(pRenderTarget->cLevels == 1);
+
+                for (uint32_t i = 0; i < pRenderTarget->cLevels; i++)
                 {
                     if (pRenderTarget->paMipmapLevels[i].fDirty)
@@ -4401,5 +4397,5 @@
                 {
                     Assert(pSurface->idAssociatedContext == SVGA3D_INVALID_ID);
-                    LogFunc(("CreateTexture (%d,%d) level=%d fUsage=%x format=%x\n", pSurface->paMipmapLevels[0].mipmapSize.width, pSurface->paMipmapLevels[0].mipmapSize.height, pSurface->faces[0].numMipLevels, pSurface->fUsageD3D, pSurface->formatD3D));
+                    LogFunc(("CreateTexture (%d,%d) level=%d fUsage=%x format=%x\n", pSurface->paMipmapLevels[0].mipmapSize.width, pSurface->paMipmapLevels[0].mipmapSize.height, pSurface->cLevels, pSurface->fUsageD3D, pSurface->formatD3D));
                     rc = vmsvga3dBackCreateTexture(pState, pContext, cid, pSurface);
                     AssertRCReturn(rc, rc);
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d.cpp
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d.cpp	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d.cpp	(revision 86838)
@@ -50,53 +50,23 @@
  * @param   surfaceFlags        .
  * @param   format              .
- * @param   face                .
  * @param   multisampleCount    .
  * @param   autogenFilter       .
- * @param   cMipLevels          .
- * @param   paMipLevelSizes     .
+ * @param   numMipLevels        .
+ * @param   pMipLevel0Size      .
  */
-int vmsvga3dSurfaceDefine(PVGASTATECC pThisCC, uint32_t sid, uint32_t surfaceFlags, SVGA3dSurfaceFormat format,
-                          SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES], uint32_t multisampleCount,
-                          SVGA3dTextureFilter autogenFilter, uint32_t cMipLevels, SVGA3dSize *paMipLevelSizes)
+int vmsvga3dSurfaceDefine(PVGASTATECC pThisCC, uint32_t sid, SVGA3dSurfaceFlags surfaceFlags, SVGA3dSurfaceFormat format,
+                          uint32_t multisampleCount, SVGA3dTextureFilter autogenFilter,
+                          uint32_t numMipLevels, SVGA3dSize const *pMipLevel0Size)
 {
     PVMSVGA3DSURFACE pSurface;
     PVMSVGA3DSTATE   pState = pThisCC->svga.p3dState;
-    AssertReturn(pState, VERR_NO_MEMORY);
-
-    Log(("vmsvga3dSurfaceDefine: sid=%u surfaceFlags=%x format=%s (%x) multiSampleCount=%d autogenFilter=%d, cMipLevels=%d size=(%d,%d,%d)\n",
-         sid, surfaceFlags, vmsvgaLookupEnum((int)format, &g_SVGA3dSurfaceFormat2String), format, multisampleCount, autogenFilter,
-         cMipLevels, paMipLevelSizes->width, paMipLevelSizes->height, paMipLevelSizes->depth));
+    AssertReturn(pState, VERR_INVALID_STATE);
+
+    LogFunc(("sid=%u surfaceFlags=%#x format=%s (%#x) multiSampleCount=%d autogenFilter=%d, numMipLevels=%d size=(%dx%dx%d)\n",
+             sid, surfaceFlags, vmsvgaLookupEnum((int)format, &g_SVGA3dSurfaceFormat2String), format, multisampleCount, autogenFilter,
+             numMipLevels, pMipLevel0Size->width, pMipLevel0Size->height, pMipLevel0Size->depth));
 
     AssertReturn(sid < SVGA3D_MAX_SURFACE_IDS, VERR_INVALID_PARAMETER);
-    AssertReturn(cMipLevels >= 1, VERR_INVALID_PARAMETER);
-
-    /* Number of faces (cFaces) is specified as the number of the first non-zero elements in the 'face' array.
-     * Since only plain surfaces (cFaces == 1) and cubemaps (cFaces == 6) are supported
-     * (see also SVGA3dCmdDefineSurface definition in svga3d_reg.h), we ignore anything else.
-     */
-    uint32_t cRemainingMipLevels = cMipLevels;
-    uint32_t cFaces = 0;
-    for (uint32_t i = 0; i < SVGA3D_MAX_SURFACE_FACES; ++i)
-    {
-        if (face[i].numMipLevels == 0)
-            break;
-
-        /* All SVGA3dSurfaceFace structures must have the same value of numMipLevels field */
-        AssertReturn(face[i].numMipLevels == face[0].numMipLevels, VERR_INVALID_PARAMETER);
-
-        /* numMipLevels value can't be greater than the number of remaining elements in the paMipLevelSizes array. */
-        AssertReturn(face[i].numMipLevels <= cRemainingMipLevels, VERR_INVALID_PARAMETER);
-        cRemainingMipLevels -= face[i].numMipLevels;
-
-        ++cFaces;
-    }
-    for (uint32_t i = cFaces; i < SVGA3D_MAX_SURFACE_FACES; ++i)
-        AssertReturn(face[i].numMipLevels == 0, VERR_INVALID_PARAMETER);
-
-    /* cFaces must be 6 for a cubemap and 1 otherwise. */
-    AssertReturn(cFaces == (uint32_t)((surfaceFlags & SVGA3D_SURFACE_CUBEMAP) ? 6 : 1), VERR_INVALID_PARAMETER);
-
-    /* Sum of face[i].numMipLevels must be equal to cMipLevels. */
-    AssertReturn(cRemainingMipLevels == 0, VERR_INVALID_PARAMETER);
+    AssertReturn(numMipLevels >= 1, VERR_INVALID_PARAMETER);
 
     if (sid >= pState->cSurfaces)
@@ -126,4 +96,6 @@
     pSurface->idWeakContextAssociation = SVGA3D_INVALID_ID;
     pSurface->oglId.buffer          = OPENGL_INVALID_ID;
+#elif defined(VMSVGA3D_DX)
+    // pSurface->pBackendSurface       = NULL;
 #else /* VMSVGA3D_DIRECT3D */
     pSurface->idAssociatedContext   = SVGA3D_INVALID_ID;
@@ -132,4 +104,8 @@
 #endif
 
+    /** @todo This 'switch' and the sufraceFlags tweaks should not be necessary.
+     * The actual surface type will be figured out when the surface is actually used later.
+     * The backends code must be reviewed for unnecessary dependencies on the surfaceFlags value.
+     */
     /* The surface type is sort of undefined now, even though the hints and format can help to clear that up.
      * In some case we'll have to wait until the surface is used to create the D3D object.
@@ -145,4 +121,5 @@
     case SVGA3D_Z_DF24:
     case SVGA3D_Z_D24S8_INT:
+        Assert(surfaceFlags & SVGA3D_SURFACE_HINT_DEPTHSTENCIL);
         surfaceFlags |= SVGA3D_SURFACE_HINT_DEPTHSTENCIL;
         break;
@@ -171,4 +148,5 @@
     case SVGA3D_A1R5G5B5:
     case SVGA3D_A4R4G4B4:
+        Assert(surfaceFlags & (SVGA3D_SURFACE_HINT_TEXTURE | SVGA3D_SURFACE_SCREENTARGET));
         surfaceFlags |= SVGA3D_SURFACE_HINT_TEXTURE;
         break;
@@ -210,19 +188,84 @@
     pSurface->surfaceFlags      = surfaceFlags;
     pSurface->format            = format;
-    memcpy(pSurface->faces, face, sizeof(pSurface->faces));
-    pSurface->cFaces            = cFaces;
+    /* cFaces is 6 for a cubemaps and 1 otherwise. */
+    pSurface->cFaces            = (uint32_t)((surfaceFlags & SVGA3D_SURFACE_CUBEMAP) ? 6 : 1);
+    pSurface->cLevels           = numMipLevels;
     pSurface->multiSampleCount  = multisampleCount;
     pSurface->autogenFilter     = autogenFilter;
     Assert(autogenFilter != SVGA3D_TEX_FILTER_FLATCUBIC);
     Assert(autogenFilter != SVGA3D_TEX_FILTER_GAUSSIANCUBIC);
-    pSurface->cMipmapLevels     = cMipLevels;
-    pSurface->paMipmapLevels    = (PVMSVGA3DMIPMAPLEVEL)RTMemAllocZ(cMipLevels * sizeof(VMSVGA3DMIPMAPLEVEL));
+    pSurface->paMipmapLevels    = (PVMSVGA3DMIPMAPLEVEL)RTMemAllocZ(numMipLevels * pSurface->cFaces * sizeof(VMSVGA3DMIPMAPLEVEL));
     AssertReturn(pSurface->paMipmapLevels, VERR_NO_MEMORY);
-
-    for (uint32_t i=0; i < cMipLevels; i++)
-        pSurface->paMipmapLevels[i].mipmapSize = paMipLevelSizes[i];
 
     pSurface->cbBlock = vmsvga3dSurfaceFormatSize(format, &pSurface->cxBlock, &pSurface->cyBlock);
     AssertReturn(pSurface->cbBlock, VERR_INVALID_PARAMETER);
+
+    /** @todo cbMemRemaining = value of SVGA_REG_MOB_MAX_SIZE */
+    uint32_t cbMemRemaining = SVGA3D_MAX_SURFACE_MEM_SIZE; /* Do not allow more than this for a surface. */
+    SVGA3dSize mipmapSize = *pMipLevel0Size;
+    for (uint32_t i = 0; i < numMipLevels; ++i)
+    {
+        for (uint32_t iFace = 0; iFace < pSurface->cFaces; ++iFace)
+        {
+            uint32_t const iMipmap = iFace * numMipLevels + i;
+            LogFunc(("[%d] face %d mip level %d (%d,%d,%d) cbBlock=%#x block %dx%d\n",
+                     iMipmap, iFace, i, mipmapSize.width, mipmapSize.height, mipmapSize.depth,
+                     pSurface->cbBlock, pSurface->cxBlock, pSurface->cyBlock));
+
+            uint32_t cBlocksX;
+            uint32_t cBlocksY;
+            if (RT_LIKELY(pSurface->cxBlock == 1 && pSurface->cyBlock == 1))
+            {
+                cBlocksX = mipmapSize.width;
+                cBlocksY = mipmapSize.height;
+            }
+            else
+            {
+                cBlocksX = mipmapSize.width / pSurface->cxBlock;
+                if (mipmapSize.width % pSurface->cxBlock)
+                    ++cBlocksX;
+                cBlocksY = mipmapSize.height / pSurface->cyBlock;
+                if (mipmapSize.height % pSurface->cyBlock)
+                    ++cBlocksY;
+            }
+
+            AssertReturn(cBlocksX > 0 && cBlocksY > 0 && mipmapSize.depth > 0, VERR_INVALID_PARAMETER);
+
+            const uint32_t cMaxBlocksX = cbMemRemaining / pSurface->cbBlock;
+            if (cBlocksX > cMaxBlocksX)
+                return VERR_INVALID_PARAMETER;
+            const uint32_t cbSurfacePitch = pSurface->cbBlock * cBlocksX;
+            LogFunc(("cbSurfacePitch=0x%x\n", cbSurfacePitch));
+
+            const uint32_t cMaxBlocksY = cbMemRemaining / cbSurfacePitch;
+            if (cBlocksY > cMaxBlocksY)
+                return VERR_INVALID_PARAMETER;
+            const uint32_t cbSurfacePlane = cbSurfacePitch * cBlocksY;
+
+            const uint32_t cMaxDepth = cbMemRemaining / cbSurfacePlane;
+            if (mipmapSize.depth > cMaxDepth)
+                return VERR_INVALID_PARAMETER;
+            const uint32_t cbSurface = cbSurfacePlane * mipmapSize.depth;
+
+            PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[iMipmap];
+            pMipmapLevel->mipmapSize     = mipmapSize;
+            pMipmapLevel->cBlocksX       = cBlocksX;
+            pMipmapLevel->cBlocksY       = cBlocksY;
+            pMipmapLevel->cBlocks        = cBlocksX * cBlocksY * mipmapSize.depth;
+            pMipmapLevel->cbSurfacePitch = cbSurfacePitch;
+            pMipmapLevel->cbSurfacePlane = cbSurfacePlane;
+            pMipmapLevel->cbSurface      = cbSurface;
+            pMipmapLevel->pSurfaceData   = NULL;
+
+            cbMemRemaining -= cbSurface;
+        }
+
+        mipmapSize.width >>= 1;
+        if (mipmapSize.width == 0) mipmapSize.width = 1;
+        mipmapSize.height >>= 1;
+        if (mipmapSize.height == 0) mipmapSize.height = 1;
+        mipmapSize.depth >>= 1;
+        if (mipmapSize.depth == 0) mipmapSize.depth = 1;
+    }
 
 #ifdef VMSVGA3D_DIRECT3D
@@ -246,4 +289,6 @@
     /* pSurface->bounce.pTexture = NULL; */
     /* pSurface->emulated.pTexture = NULL; */
+#elif defined(VMSVGA3D_DX)
+    /* Nothing, because all backend specific data reside in pSurface->pBackendSurface. */
 #else
     /* pSurface->fEmulated = false; */
@@ -264,61 +309,9 @@
 
     /* Allocate buffer to hold the surface data until we can move it into a D3D object */
-    uint32_t cbMemRemaining = SVGA3D_MAX_SURFACE_MEM_SIZE; /* Do not allow more than this for a surface. */
-    for (uint32_t i = 0; i < cMipLevels; ++i)
+    for (uint32_t i = 0; i < numMipLevels * pSurface->cFaces; ++i)
     {
         PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->paMipmapLevels[i];
-        LogFunc(("[%d] face %d mip level %d (%d,%d,%d) cbBlock=0x%x block %dx%d\n",
-                 i, i / pSurface->faces[0].numMipLevels, i % pSurface->faces[0].numMipLevels,
-                 pMipmapLevel->mipmapSize.width, pMipmapLevel->mipmapSize.height, pMipmapLevel->mipmapSize.depth,
-                 pSurface->cbBlock, pSurface->cxBlock, pSurface->cyBlock));
-
-        uint32_t cBlocksX;
-        uint32_t cBlocksY;
-        if (RT_LIKELY(pSurface->cxBlock == 1 && pSurface->cyBlock == 1))
-        {
-            cBlocksX = pMipmapLevel->mipmapSize.width;
-            cBlocksY = pMipmapLevel->mipmapSize.height;
-        }
-        else
-        {
-            cBlocksX = pMipmapLevel->mipmapSize.width / pSurface->cxBlock;
-            if (pMipmapLevel->mipmapSize.width % pSurface->cxBlock)
-                ++cBlocksX;
-            cBlocksY = pMipmapLevel->mipmapSize.height / pSurface->cyBlock;
-            if (pMipmapLevel->mipmapSize.height % pSurface->cyBlock)
-                ++cBlocksY;
-        }
-
-        if (   cBlocksX == 0
-            || cBlocksY == 0
-            || pMipmapLevel->mipmapSize.depth == 0)
-            return VERR_INVALID_PARAMETER;
-
-        const uint32_t cMaxBlocksX = cbMemRemaining / pSurface->cbBlock;
-        if (cBlocksX > cMaxBlocksX)
-            return VERR_INVALID_PARAMETER;
-        const uint32_t cbSurfacePitch = pSurface->cbBlock * cBlocksX;
-        LogFunc(("cbSurfacePitch=0x%x\n", cbSurfacePitch));
-
-        const uint32_t cMaxBlocksY = cbMemRemaining / cbSurfacePitch;
-        if (cBlocksY > cMaxBlocksY)
-            return VERR_INVALID_PARAMETER;
-        const uint32_t cbSurfacePlane = cbSurfacePitch * cBlocksY;
-
-        const uint32_t cMaxDepth = cbMemRemaining / cbSurfacePlane;
-        if (pMipmapLevel->mipmapSize.depth > cMaxDepth)
-            return VERR_INVALID_PARAMETER;
-        const uint32_t cbSurface = cbSurfacePlane * pMipmapLevel->mipmapSize.depth;
-
-        pMipmapLevel->cBlocksX       = cBlocksX;
-        pMipmapLevel->cBlocksY       = cBlocksY;
-        pMipmapLevel->cBlocks        = cBlocksX * cBlocksY * pMipmapLevel->mipmapSize.depth;
-        pMipmapLevel->cbSurfacePitch = cbSurfacePitch;
-        pMipmapLevel->cbSurfacePlane = cbSurfacePlane;
-        pMipmapLevel->cbSurface      = cbSurface;
-        pMipmapLevel->pSurfaceData   = RTMemAllocZ(cbSurface);
+        pMipmapLevel->pSurfaceData = RTMemAllocZ(pMipmapLevel->cbSurface);
         AssertReturn(pMipmapLevel->pSurfaceData, VERR_NO_MEMORY);
-
-        cbMemRemaining -= cbSurface;
     }
     return VINF_SUCCESS;
@@ -363,5 +356,5 @@
     if (pSurface->paMipmapLevels)
     {
-        for (uint32_t i = 0; i < pSurface->cMipmapLevels; ++i)
+        for (uint32_t i = 0; i < pSurface->cLevels * pSurface->cFaces; ++i)
             RTMemFreeZ(pSurface->paMipmapLevels[i].pSurfaceData, pSurface->paMipmapLevels[i].cbSurface);
         RTMemFree(pSurface->paMipmapLevels);
@@ -405,9 +398,8 @@
     AssertRCReturn(rc, rc);
 
-    /* Can use faces[0].numMipLevels, because numMipLevels is the same for all faces. */
     AssertReturn(pSrcSfcImg->face < pSrcSurface->cFaces, VERR_INVALID_PARAMETER);
-    AssertReturn(pSrcSfcImg->mipmap < pSrcSurface->faces[0].numMipLevels, VERR_INVALID_PARAMETER);
+    AssertReturn(pSrcSfcImg->mipmap < pSrcSurface->cLevels, VERR_INVALID_PARAMETER);
     AssertReturn(pDstSfcImg->face < pDstSurface->cFaces, VERR_INVALID_PARAMETER);
-    AssertReturn(pDstSfcImg->mipmap < pDstSurface->faces[0].numMipLevels, VERR_INVALID_PARAMETER);
+    AssertReturn(pDstSfcImg->mipmap < pDstSurface->cLevels, VERR_INVALID_PARAMETER);
 
     PVMSVGA3DCONTEXT pContext;
@@ -514,5 +506,6 @@
         /* Flush the drawing pipeline for this surface as it could be used in a shared context. */
         vmsvga3dSurfaceFlush(pSurface);
-
+#elif defined(VMSVGA3D_DX)
+        /** @todo */
 #else /* VMSVGA3D_OPENGL */
         pContext = &pState->SharedCtx;
Index: /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d.h
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d.h	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGA-SVGA3d.h	(revision 86838)
@@ -49,4 +49,26 @@
 
 
+typedef enum VMSVGA3D_SURFACE_MAP
+{
+    VMSVGA3D_SURFACE_MAP_READ,
+    VMSVGA3D_SURFACE_MAP_WRITE,
+    VMSVGA3D_SURFACE_MAP_READ_WRITE,
+    VMSVGA3D_SURFACE_MAP_WRITE_DISCARD,
+} VMSVGA3D_SURFACE_MAP;
+
+typedef struct VMSVGA3D_MAPPED_SURFACE
+{
+    VMSVGA3D_SURFACE_MAP enmMapType;
+    SVGA3dBox box;
+    uint32_t cbPixel;
+    uint32_t cbRowPitch;
+    uint32_t cbDepthPitch;
+    void *pvData;
+} VMSVGA3D_MAPPED_SURFACE;
+
+#ifdef DUMP_BITMAPS
+void vmsvga3dMapWriteBmpFile(VMSVGA3D_MAPPED_SURFACE const *pMap, char const *pszPrefix);
+#endif
+
 /* DevVGA-SVGA.cpp: */
 void vmsvgaR33dSurfaceUpdateHeapBuffersOnFifoThread(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t sid);
@@ -63,7 +85,7 @@
 int vmsvga3dQueryCaps(PVGASTATECC pThisCC, uint32_t idx3dCaps, uint32_t *pu32Val);
 
-int vmsvga3dSurfaceDefine(PVGASTATECC pThisCC, uint32_t sid, uint32_t surfaceFlags, SVGA3dSurfaceFormat format,
-                          SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES], uint32_t multisampleCount,
-                          SVGA3dTextureFilter autogenFilter, uint32_t cMipLevels, SVGA3dSize *pMipLevelSize);
+int vmsvga3dSurfaceDefine(PVGASTATECC pThisCC, uint32_t sid, SVGA3dSurfaceFlags surfaceFlags, SVGA3dSurfaceFormat format,
+                          uint32_t multisampleCount, SVGA3dTextureFilter autogenFilter,
+                          uint32_t cMipLevels, SVGA3dSize const *pMipLevel0Size);
 int vmsvga3dSurfaceDestroy(PVGASTATECC pThisCC, uint32_t sid);
 int vmsvga3dSurfaceCopy(PVGASTATECC pThisCC, SVGA3dSurfaceImageId dest, SVGA3dSurfaceImageId src,
@@ -108,4 +130,10 @@
 int vmsvga3dQueryWait(PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t cid, SVGA3dQueryType type, SVGAGuestPtr guestResult);
 
+int vmsvga3dScreenTargetBind(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, uint32_t sid);
+int vmsvga3dScreenTargetUpdate(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, SVGA3dRect const *pRect);
+
+int vmsvga3dSurfaceMap(PVGASTATECC pThisCC, SVGA3dSurfaceImageId const *pImage, SVGA3dBox const *pBox, VMSVGA3D_SURFACE_MAP enmMapType, VMSVGA3D_MAPPED_SURFACE *pMap);
+int vmsvga3dSurfaceUnmap(PVGASTATECC pThisCC, SVGA3dSurfaceImageId const *pImage, VMSVGA3D_MAPPED_SURFACE *pMap, bool fWritten);
+
 /* DevVGA-SVGA3d-shared.h: */
 #if defined(RT_OS_WINDOWS) && defined(IN_RING3)
Index: /trunk/src/VBox/Devices/Graphics/DevVGASavedState.h
===================================================================
--- /trunk/src/VBox/Devices/Graphics/DevVGASavedState.h	(revision 86837)
+++ /trunk/src/VBox/Devices/Graphics/DevVGASavedState.h	(revision 86838)
@@ -47,5 +47,6 @@
     } while (0)
 
-#define VGA_SAVEDSTATE_VERSION                   22
+#define VGA_SAVEDSTATE_VERSION                   23
+#define VGA_SAVEDSTATE_VERSION_VMSVGA_MIPLEVELS  23 /* Surface struct with number of miplevels. */
 #define VGA_SAVEDSTATE_VERSION_VMSVGA_CURSOR     22 /* Legacy cursor registers. */
 #define VGA_SAVEDSTATE_VERSION_VMSVGA_SCREENS    21 /* Screen objects. */
Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 86837)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 86838)
@@ -320,10 +320,16 @@
   	Graphics/DevVGA-SVGA3d-savedstate.cpp
   if  "$(KBUILD_TARGET)" == "win" && !defined(VBOX_WITH_VMSVGA3D_USE_OPENGL)
-   VBoxDD_DEFS          += VMSVGA3D_DIRECT3D
-   VBoxDD_SOURCES       += \
-       Graphics/DevVGA-SVGA3d-win.cpp \
-       Graphics/DevVGA-SVGA3d-win-d3d9.cpp
-   VBoxDD_LIBS.win      += d3d9.lib $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/delayimp.lib
-   VBoxDD_LDFLAGS.win   += /DELAYLOAD:d3d9.dll
+   ifdef VBOX_WITH_VMSVGA3D_DX
+    VBoxDD_DEFS          += VMSVGA3D_DX
+    VBoxDD_SOURCES       += \
+        Graphics/DevVGA-SVGA3d-win-dx.cpp
+   else
+    VBoxDD_DEFS          += VMSVGA3D_DIRECT3D
+    VBoxDD_SOURCES       += \
+        Graphics/DevVGA-SVGA3d-win.cpp \
+        Graphics/DevVGA-SVGA3d-win-d3d9.cpp
+    VBoxDD_LIBS.win      += d3d9.lib $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/delayimp.lib
+    VBoxDD_LDFLAGS.win   += /DELAYLOAD:d3d9.dll
+   endif
   else
    VBoxDD_DEFS          += VMSVGA3D_OPENGL
