Index: /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVRDP.cpp
===================================================================
--- /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVRDP.cpp	(revision 45767)
+++ /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVRDP.cpp	(revision 45768)
@@ -6,5 +6,5 @@
 
 /*
- * Copyright (C) 2011-2012 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -1461,5 +1461,5 @@
                 int cacheResult;
 
-                LOG(("MEMBLT."));
+                LOG(("MEMBLT: bitmap %dx%d.", psoSrc->sizlBitmap.cx, psoSrc->sizlBitmap.cy));
                 if (   (psoSrc->fjBitmap & BMF_DONTCACHE) != 0
                     || psoSrc->iUniq == 0
@@ -1470,4 +1470,8 @@
                         && !(psoSrc->iBitmapFormat == BMF_24BPP || psoSrc->iBitmapFormat == BMF_32BPP)
                        )
+                       /* Do not try to cache large bitmaps. The cache should be mostly used for icons, etc.
+                        * Computing a bitmap hash increases CPU load. Up to 384K pixels (~620x620)
+                        */
+                    || psoSrc->sizlBitmap.cx * psoSrc->sizlBitmap.cy > 384 * _1K
                    )
                 {
@@ -1477,15 +1481,9 @@
                 else
                 {
-                    LOG(("going to cache."));
-                    cacheResult = vrdpbmpCacheSurface(&pDev->vrdpCache, psoSrc, &hash, &hashDeleted);
-                }
-
-                LOG(("MEMBLT: cacheResult 0x%08X. trg %d,%d %dx%d src %dx%d from %d,%d",
-                         cacheResult,
-                         rclTrg.left, rclTrg.top,
-                         rclTrg.right - rclTrg.left,
-                         rclTrg.bottom - rclTrg.top,
-                         psoSrc->sizlBitmap.cx, psoSrc->sizlBitmap.cy,
-                         pptlSrc->x, pptlSrc->y));
+                    LOG(("MEMBLT: going to cache."));
+                    cacheResult = vrdpbmpCacheSurface(&pDev->vrdpCache, psoSrc, &hash, &hashDeleted, FALSE);
+                }
+
+                LOG(("MEMBLT: cacheResult 0x%08X", cacheResult));
 
                 if (cacheResult & VRDPBMP_RC_F_DELETED)
@@ -1500,8 +1498,15 @@
                     case VRDPBMP_RC_CACHED:
                         vrdpReportCachedBitmap(pDev, psoSrc, &hash);
+                        LOG(("MEMBLT: cached add %dx%d",
+                             psoSrc->sizlBitmap.cx, psoSrc->sizlBitmap.cy));
                         /* Continue and report MEMBLT order. */
 
                     case VRDPBMP_RC_ALREADY_CACHED:
                         vrdpReportMemBlt(pDev, &clipRects, pptlSrc, (uint8_t)rop4, &hash);
+                        LOG(("MEMBLT: cached use %dx%d from %d,%d %dx%d",
+                             psoSrc->sizlBitmap.cx, psoSrc->sizlBitmap.cy,
+                             pptlSrc->x, pptlSrc->y,
+                             rclTrg.right - rclTrg.left,
+                             rclTrg.bottom - rclTrg.top));
                         LOG(("        %08X %08X %08X %08X",
                                  *(uint32_t *)&((uint8_t *)&hash)[0],
@@ -1514,5 +1519,9 @@
                     default:
                         /* The surface was not cached. Fallback to dirty rects. */
-                        LOG(("MEMBLT: bitmap not cached."));
+                        LOG(("MEMBLT: not cached %dx%d from %d,%d %dx%d",
+                             psoSrc->sizlBitmap.cx, psoSrc->sizlBitmap.cy,
+                             pptlSrc->x, pptlSrc->y,
+                             rclTrg.right - rclTrg.left,
+                             rclTrg.bottom - rclTrg.top));
                         VBoxDispDumpPSO(psoSrc, "psoSrc");
                         vrdpReportDirtyRects(pDev, &clipRects);
Index: /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.cpp
===================================================================
--- /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.cpp	(revision 45767)
+++ /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.cpp	(revision 45768)
@@ -6,5 +6,5 @@
 
 /*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -22,7 +22,14 @@
 
 /*
- * Cache has a fixed number of preallocated entries. Entries are linked in the MRU
- * list. The list contains both used and free entries. Free entries are at the end.
- * The most recently used entry is in the head.
+ * Cache has a fixed number of preallocated entries. Entries are linked in the MRU lists.
+ *
+ * A new bitmap hash is added to the "temporary" list, and the caller is told that the
+ * bitmap was not cached. If the hash is used again, then it is moved to the "cached" list.
+ * This protects against "cache, memblt, cache, memblt, ..." sequences.
+ *
+ * "Temporary" list contains free and temporary entries. Temporary entries are at the head,
+ * free entries are at the tail. New temporary entries are inserted in the head.
+ * 
+ * "Cached" list contains cached entries. When a entry is used, it is moved to the head.
  *
  * The purpose of the cache is to answer whether the bitmap was already encountered
@@ -75,48 +82,140 @@
 }
 
-/* Meves an entry to the head of MRU list. */
-static void bcMoveToHead (VRDPBC *pCache, VRDPBCENTRY *pEntry)
+static void bcRemoveFromCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
 {
     if (pEntry->prev)
     {
+        pEntry->prev->next = pEntry->next;
+    }
+    else
+    {
+        pCache->headCached = pEntry->next;
+    }
+
+    if (pEntry->next)
+    {
+        pEntry->next->prev = pEntry->prev;
+    }
+    else
+    {
+        pCache->tailCached = pEntry->prev;
+    }
+}
+
+static void bcRemoveFromTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    if (pEntry->prev)
+    {
+        pEntry->prev->next = pEntry->next;
+    }
+    else
+    {
+        pCache->headTmp = pEntry->next;
+    }
+
+    if (pEntry->next)
+    {
+        pEntry->next->prev = pEntry->prev;
+    }
+    else
+    {
+        pCache->tailTmp = pEntry->prev;
+    }
+}
+
+static void bcInsertHeadCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    pEntry->prev = NULL;
+    pEntry->next = pCache->headCached;
+
+    if (pCache->headCached)
+    {
+        pCache->headCached->prev = pEntry;
+    }
+    else
+    {
+        pCache->tailCached = pEntry;
+    }
+
+    pCache->headCached = pEntry;
+}
+
+static void bcInsertHeadTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    pEntry->prev = NULL;
+    pEntry->next = pCache->headTmp;
+
+    if (pCache->headTmp)
+    {
+        pCache->headTmp->prev = pEntry;
+    }
+    else
+    {
+        pCache->tailTmp = pEntry;
+    }
+
+    pCache->headTmp = pEntry;
+}
+
+/* Moves an entry to the head of MRU list. */
+static void bcMoveToHeadCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    if (pEntry->prev)
+    {
         /* The entry is not yet in the head. Exclude from list. */
-        pEntry->prev->next = pEntry->next;
-
-        if (pEntry->next)
-        {
-            pEntry->next->prev = pEntry->prev;
-        }
-        else
-        {
-            pCache->tail = pEntry->prev;
-        }
+        bcRemoveFromCached(pCache, pEntry);
 
         /* Insert the entry at the head of MRU list. */
-        pEntry->prev = NULL;
-        pEntry->next = pCache->head;
-
-        Assert(pCache->head);
-
-        pCache->head->prev = pEntry;
-        pCache->head = pEntry;
-    }
-}
-
-/* Returns TRUE if the hash already presents in the cache.
- * Moves the found entry to the head of MRU list.
- */
-static BOOL bcFindHash (VRDPBC *pCache, const VRDPBCHASH *phash)
-{
-    /* Search the MRU list. */
-    VRDPBCENTRY *pEntry = pCache->head;
-
-    while (pEntry && pEntry->fUsed)
+        bcInsertHeadCached(pCache, pEntry);
+    }
+}
+
+static void bcMoveToHeadTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    if (pEntry->prev)
+    {
+        /* The entry is not yet in the head. Exclude from list. */
+        bcRemoveFromTmp(pCache, pEntry);
+
+        /* Insert the entry at the head of MRU list. */
+        bcInsertHeadTmp(pCache, pEntry);
+    }
+}
+
+static void bcMoveTmpToCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    /* Remove from Tmp list. */
+    bcRemoveFromTmp(pCache, pEntry);
+
+    /* Insert the entry at the head of Cached list. */
+    bcInsertHeadCached(pCache, pEntry);
+}
+
+static void bcMoveCachedToTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
+{
+    /* Remove from cached list. */
+    bcRemoveFromCached(pCache, pEntry);
+
+    /* Insert the entry at the head of Tmp list. */
+    bcInsertHeadTmp(pCache, pEntry);
+}
+
+
+/* Returns pointer to the entry if the hash already presents in the cache.
+ * Moves the found entry to the head of cached MRU list.
+ */
+static VRDPBCENTRY *bcFindHash (VRDPBC *pCache, const VRDPBCHASH *phash)
+{
+    /* Search the "Cached" MRU list. */
+    VRDPBCENTRY *pEntry = pCache->headCached;
+
+    while (pEntry)
     {
         if (memcmp (&pEntry->hash, phash, sizeof (VRDPBCHASH)) == 0)
         {
-            /* Found the entry. Move it to the head of MRU list. */
-            bcMoveToHead (pCache, pEntry);
-
-            return TRUE;
+            /* Found the entry. Move it to the head of Cached MRU list. */
+            bcMoveToHeadCached(pCache, pEntry);
+
+            return pEntry;
         }
 
@@ -124,50 +223,107 @@
     }
 
-    return FALSE;
-}
-
-/* Returns TRUE is a entry was also deleted to nake room for new entry. */
-static BOOL bcInsertHash (VRDPBC *pCache, const VRDPBCHASH *phash, VRDPBCHASH *phashDeleted)
-{
-    BOOL bRc = FALSE;
-    VRDPBCENTRY *pEntry;
-
-    LOG(("insert hash cache %p, tail %p.", pCache, pCache->tail));
-
-    /* Get the free entry to be used. Try tail, that should be */
-    pEntry = pCache->tail;
-
-    if (pEntry == NULL)
-    {
-        return bRc;
-    }
-
-    if (pEntry->fUsed)
-    {
-        /* The cache is full. Remove the tail. */
+    /* Search the "Temporary" MRU list. */
+    pEntry = pCache->headTmp;
+
+    while (   pEntry
+           && pEntry->u32Status != VRDP_BC_ENTRY_STATUS_EMPTY)
+    {
+        if (memcmp (&pEntry->hash, phash, sizeof (VRDPBCHASH)) == 0)
+        {
+            /* Found the entry. It will be removed from the list by the caller. */
+            return pEntry;
+        }
+
+        pEntry = pEntry->next;
+    }
+
+    return NULL;
+}
+
+/* Returns TRUE is a entry was also deleted to make room for new entry. */
+static int bcInsertHash (VRDPBC *pCache, const VRDPBCHASH *phash, VRDPBCHASH *phashDeleted, BOOL bForce)
+{
+    LOG(("bcInsertHash %p, tmp tail %p, cached tail %p.", pCache, pCache->tailTmp, pCache->tailCached));
+
+    /* Get the free entry to be used. Try Tmp list, then the tail of the Cached list. */
+    VRDPBCENTRY *pEntry = pCache->tailTmp;
+
+    if (pEntry != NULL)
+    {
+        /* Insert to the head of Tmp list. */
+        bcMoveToHeadTmp(pCache, pEntry);
+        LOG(("bcInsertHash %p, use tmp tail %p.", pCache, pEntry));
+    }
+    else
+    {
+        pEntry = pCache->tailCached;
+        LOG(("bcInsertHash %p, reuse cached tail %p.", pCache, pEntry, pEntry? pEntry->u32Status: 0));
+
+        if (pEntry != NULL)
+        {
+            bcMoveCachedToTmp(pCache, pEntry);
+        }
+    }
+
+    if (!pEntry)
+    {
+        LOG(("bcInsertHash %p, failed to find an entry!!!", pCache));
+        return VRDPBMP_RC_NOT_CACHED;
+    }
+
+    BOOL bHashDeleted;
+    if (pEntry->u32Status == VRDP_BC_ENTRY_STATUS_CACHED)
+    {
+        /* The cache is full. Remove the tail hash. */
         memcpy (phashDeleted, &pEntry->hash, sizeof (VRDPBCHASH));
-        bRc = TRUE;
-    }
-
-    bcMoveToHead (pCache, pEntry);
-
+        bHashDeleted = TRUE;
+    }
+    else
+    {
+        bHashDeleted = FALSE;
+    }
+
+    /* The just inserted entry is at the head of Tmp list, so the temporary
+     * entries will be deleted when there is no room in the cache.
+     */
     memcpy (&pEntry->hash, phash, sizeof (VRDPBCHASH));
-    pEntry->fUsed = TRUE;
-
-    return bRc;
+
+    int rc;
+    if (bForce)
+    {
+        LOG(("Force cache"));
+        bcMoveTmpToCached(pCache, pEntry);
+        pEntry->u32Status = VRDP_BC_ENTRY_STATUS_CACHED;
+        rc = VRDPBMP_RC_CACHED;
+    }
+    else
+    {
+        pEntry->u32Status = VRDP_BC_ENTRY_STATUS_TEMPORARY;
+        rc = VRDPBMP_RC_NOT_CACHED;
+    }
+
+    if (bHashDeleted)
+    {
+        rc |= VRDPBMP_RC_F_DELETED;
+    }
+
+    return rc;
 }
 
 /* Find out whether the surface already in the cache.
  * Insert in the cache if not.
- */
-int vrdpbmpCacheSurface(VRDPBC *pCache, const SURFOBJ *pso, VRDPBCHASH *phash, VRDPBCHASH *phashDeleted)
-{
-    int rc;
-
+ * Protection against "cache, memblt, cache, memblt, ..." sequence:
+ *    first time just append the bitmap hash and mark it as "temporary";
+ *    if the hash is used again, mark as cached and tell the caller to cache the bitmap;
+ *    remove "temporary" entries before any other.
+ *    
+ */
+int vrdpbmpCacheSurface(VRDPBC *pCache, const SURFOBJ *pso, VRDPBCHASH *phash, VRDPBCHASH *phashDeleted, BOOL bForce)
+{
     VRDPBCHASH hash;
 
     BOOL bResult = bcComputeHash (pso, &hash);
-
     LOG(("vrdpbmpCacheSurface: compute hash %d.", bResult));
+
     if (!bResult)
     {
@@ -176,23 +332,33 @@
     }
 
-    bResult = bcFindHash (pCache, &hash);
-
-    LOG(("vrdpbmpCacheSurface: find hash %d.", bResult));
     *phash = hash;
 
-    if (bResult)
-    {
-        return VRDPBMP_RC_ALREADY_CACHED;
-    }
-
-    rc = VRDPBMP_RC_CACHED;
-
-    bResult = bcInsertHash (pCache, &hash, phashDeleted);
-
-    LOG(("vrdpbmpCacheSurface: insert hash %d.", bResult));
-    if (bResult)
-    {
-        rc |= VRDPBMP_RC_F_DELETED;
-    }
+    VRDPBCENTRY *pEntry = bcFindHash (pCache, &hash);
+    LOG(("vrdpbmpCacheSurface: find hash %d.", pEntry? pEntry->u32Status: 0));
+
+    if (pEntry)
+    {
+        if (pEntry->u32Status == VRDP_BC_ENTRY_STATUS_CACHED)
+        {
+            return VRDPBMP_RC_ALREADY_CACHED;
+        }
+
+        /* The status must be VRDP_BC_ENTRY_STATUS_TEMPORARY here.
+         * Update it to *_CACHED.
+         */
+        if (pEntry->u32Status != VRDP_BC_ENTRY_STATUS_TEMPORARY)
+        {
+            LOG(("MEMBLT: vrdpbmpCacheSurface: unexpected status %d.", pEntry->u32Status));
+            return VRDPBMP_RC_NOT_CACHED;
+        }
+
+        bcMoveTmpToCached(pCache, pEntry);
+
+        pEntry->u32Status = VRDP_BC_ENTRY_STATUS_CACHED;
+        return VRDPBMP_RC_CACHED;
+    }
+
+    int rc = bcInsertHash (pCache, &hash, phashDeleted, bForce);
+    LOG(("vrdpbmpCacheSurface: insert hash %x.", rc));
 
     return rc;
@@ -206,9 +372,11 @@
     Assert(sizeof (VRDPBCHASH) == sizeof (VRDEBITMAPHASH));
 
+    LOG(("vrdpbmpReset: %p.", pCache));
+
     /* Reinitialize the cache structure. */
     memset(pCache, 0, sizeof (VRDPBC));
 
-    pCache->head = &pCache->aEntries[0];
-    pCache->tail = &pCache->aEntries[RT_ELEMENTS(pCache->aEntries) - 1];
+    pCache->headTmp = &pCache->aEntries[0];
+    pCache->tailTmp = &pCache->aEntries[RT_ELEMENTS(pCache->aEntries) - 1];
 
     for (i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
@@ -216,13 +384,16 @@
         VRDPBCENTRY *pEntry = &pCache->aEntries[i];
 
-        if (pEntry != pCache->tail)
+        if (pEntry != pCache->tailTmp)
         {
             pEntry->next = &pCache->aEntries[i + 1];
         }
 
-        if (pEntry != pCache->head)
+        if (pEntry != pCache->headTmp)
         {
             pEntry->prev = &pCache->aEntries[i - 1];
         }
     }
-}
+
+    pCache->headCached = NULL;
+    pCache->tailCached = NULL;
+}
Index: /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.h
===================================================================
--- /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.h	(revision 45767)
+++ /trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.h	(revision 45768)
@@ -6,5 +6,5 @@
 
 /*
- * Copyright (C) 2011 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -56,21 +56,27 @@
 #pragma pack ()
 
+#define VRDP_BC_ENTRY_STATUS_EMPTY     0
+#define VRDP_BC_ENTRY_STATUS_TEMPORARY 1
+#define VRDP_BC_ENTRY_STATUS_CACHED    2
+
 typedef struct _VRDPBCENTRY
 {
-    bool fUsed;
     struct _VRDPBCENTRY *next;
     struct _VRDPBCENTRY *prev;
     VRDPBCHASH hash;
+    uint32_t u32Status;
 } VRDPBCENTRY;
 
 typedef struct _VRDPBC
 {
-    VRDPBCENTRY *head;
-    VRDPBCENTRY *tail;
+    VRDPBCENTRY *headTmp;
+    VRDPBCENTRY *tailTmp;
+    VRDPBCENTRY *headCached;
+    VRDPBCENTRY *tailCached;
     VRDPBCENTRY aEntries[VRDPBMP_N_CACHED_BITMAPS];
 } VRDPBC;
 
 void vrdpbmpReset (VRDPBC *pCache);
-int vrdpbmpCacheSurface (VRDPBC *pCache, const SURFOBJ *pso, VRDPBCHASH *phash, VRDPBCHASH *phashDeleted);
+int vrdpbmpCacheSurface (VRDPBC *pCache, const SURFOBJ *pso, VRDPBCHASH *phash, VRDPBCHASH *phashDeleted, BOOL bForce);
 
 #endif /*VBOXDISPVRDPBMP_H*/
