Index: trunk/src/VBox/Additions/WINNT/Graphics/Display/Makefile.kmk
===================================================================
--- trunk/src/VBox/Additions/WINNT/Graphics/Display/Makefile.kmk	(revision 4150)
+++ trunk/src/VBox/Additions/WINNT/Graphics/Display/Makefile.kmk	(revision 4181)
@@ -28,4 +28,5 @@
 VBoxDisp_DEFS        = _WIN32_WINNT=0x0501 LOG_TO_BACKDOOR VBOX_WITH_DDRAW VBOX_WITH_OPENGL
 VBoxDisp_INCS        = ../../include
+VBoxDisp_DEFS        += VBOX_VBVA_ADJUST_RECT
 #VBoxDisp_DEFS        += LOG_ENABLED
 #VBoxDisp_DEFS        += STAT_sunlover
Index: trunk/src/VBox/Additions/WINNT/Graphics/Display/driver.h
===================================================================
--- trunk/src/VBox/Additions/WINNT/Graphics/Display/driver.h	(revision 4150)
+++ trunk/src/VBox/Additions/WINNT/Graphics/Display/driver.h	(revision 4181)
@@ -231,4 +231,10 @@
     return 0;
 }
+
+#ifdef VBOX_VBVA_ADJUST_RECT
+void vrdpAdjustRect (SURFOBJ *pso, RECTL *prcl);
+BOOL vbvaFindChangedRect (SURFOBJ *psoDest, SURFOBJ *psoSrc, RECTL *prclDest, POINTL *pptlSrc);
+#endif /* VBOX_VBVA_ADJUST_RECT */
+
 
 #include <iprt/assert.h>
Index: trunk/src/VBox/Additions/WINNT/Graphics/Display/drv.c
===================================================================
--- trunk/src/VBox/Additions/WINNT/Graphics/Display/drv.c	(revision 4150)
+++ trunk/src/VBox/Additions/WINNT/Graphics/Display/drv.c	(revision 4181)
@@ -269,5 +269,9 @@
     )
 {
+    RECTL rclDest = *prclDest;
+    POINTL ptlSrc = *pptlSrc;
+
     BOOL bRc;
+    BOOL bDo = TRUE;
 
     DISPDBG((1, "%s\n", __FUNCTION__));
@@ -282,10 +286,56 @@
 
     STATPRINT;
-
-    bRc = EngCopyBits(CONV_SURF(psoDest), CONV_SURF(psoSrc), pco, pxlo, prclDest, pptlSrc);
+    
+#ifdef VBOX_VBVA_ADJUST_RECT
+    /* Experimental fix for too large bitmap updates. 
+     *
+     * Some application do a large bitmap update event if only
+     * a small part of the bitmap is actually changed.
+     *
+     * The driver will find the changed rectangle by comparing
+     * the current framebuffer content with the source bitmap.
+     *
+     * The optimization is only active when: 
+     *  - the VBVA extension is enabled;
+     *  - the source bitmap is not cacheable;
+     *  - the bitmap formats of both the source and the screen surfaces are equal.
+     *
+     */
+    if (   psoSrc
+        && !bIsScreenSurface(psoSrc)
+        && bIsScreenSurface(psoDest))
+    {
+        PPDEV ppdev = (PPDEV)psoDest->dhpdev;
+
+        VBVAMEMORY *pVbvaMemory = ppdev->vbva.pVbvaMemory;
+
+        DISPDBG((1, "offscreen->screen\n"));
+
+        if (   pVbvaMemory
+            && (pVbvaMemory->fu32ModeFlags & VBVA_F_MODE_ENABLED))
+        {
+            if (   (psoSrc->fjBitmap & BMF_DONTCACHE) != 0
+                || psoSrc->iUniq == 0)
+            {
+                DISPDBG((1, "non-cacheable %d->%d (ppdev %p)\n", psoSrc->iBitmapFormat, psoDest->iBitmapFormat, ppdev));
+
+                /* It is possible to apply the fix. */
+                bDo = vbvaFindChangedRect (CONV_SURF(psoDest), CONV_SURF(psoSrc), &rclDest, &ptlSrc);
+            }
+        }
+    }
+
+    if (!bDo)
+    {
+        /* The operation is a NOP. Just return success. */
+        return TRUE;
+    }
+#endif /* VBOX_VBVA_ADJUST_RECT */
+
+    bRc = EngCopyBits(CONV_SURF(psoDest), CONV_SURF(psoSrc), pco, pxlo, &rclDest, &ptlSrc);
 
     VBVA_OPERATION(psoDest,
                    CopyBits,
-                   (psoDest, psoSrc, pco, pxlo, prclDest, pptlSrc));
+                   (psoDest, psoSrc, pco, pxlo, &rclDest, &ptlSrc));
 
     return bRc;
Index: trunk/src/VBox/Additions/WINNT/Graphics/Display/vbva.c
===================================================================
--- trunk/src/VBox/Additions/WINNT/Graphics/Display/vbva.c	(revision 4150)
+++ trunk/src/VBox/Additions/WINNT/Graphics/Display/vbva.c	(revision 4181)
@@ -18,4 +18,286 @@
 #include "driver.h"
 
+#ifdef VBOX_VBVA_ADJUST_RECT
+static ULONG vbvaConvertPixel ( /*from*/ BYTE *pu8PixelFrom, int cbPixelFrom,
+                                /*to*/ int cbPixelTo)
+{
+    BYTE r, g, b;
+    ULONG ulConvertedPixel = 0;
+
+    switch (cbPixelFrom)
+    {
+        case 4:
+        {
+           switch (cbPixelTo)
+           {
+               case 3:
+               {
+                   memcpy (&ulConvertedPixel, pu8PixelFrom, 3);
+               } break;
+
+               case 2:
+               {
+                   ulConvertedPixel = *(ULONG *)pu8PixelFrom;
+
+                   r = (BYTE)(ulConvertedPixel >> 16);
+                   g = (BYTE)(ulConvertedPixel >> 8);
+                   b = (BYTE)(ulConvertedPixel);
+
+                   ulConvertedPixel = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
+               } break;
+           }
+        } break;
+
+        case 3:
+        {
+           switch (cbPixelTo)
+           {
+               case 2:
+               {
+                   memcpy (&ulConvertedPixel, pu8PixelFrom, 3);
+
+                   r = (BYTE)(ulConvertedPixel >> 16);
+                   g = (BYTE)(ulConvertedPixel >> 8);
+                   b = (BYTE)(ulConvertedPixel);
+
+                   ulConvertedPixel = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
+               } break;
+           }
+        } break;
+    }
+
+    return ulConvertedPixel;
+}
+
+BOOL vbvaFindChangedRect (SURFOBJ *psoDest, SURFOBJ *psoSrc, RECTL *prclDest, POINTL *pptlSrc)
+{
+    int x, y;
+    int fTopNonEqualFound;
+    int yTopmost;
+    int yBottommost;
+    int cbPixelSrc;
+    int cbPixelDest;
+    RECTL rclDest;
+    RECTL rclSrc;
+    BYTE *pu8Src;
+    BYTE *pu8Dest;
+    
+    if (!prclDest || !pptlSrc)
+    {
+        return TRUE;
+    }
+    
+    DISPDBG((1, "vbvaFindChangedRect: dest %d,%d %dx%d from %d,%d\n",
+             prclDest->left, prclDest->top, prclDest->right - prclDest->left, prclDest->bottom - prclDest->top,
+             pptlSrc->x, pptlSrc->y
+            ));
+            
+    switch (psoDest->iBitmapFormat)
+    {
+        case BMF_16BPP: cbPixelDest = 2; break;
+        case BMF_24BPP: cbPixelDest = 3; break;
+        case BMF_32BPP: cbPixelDest = 4; break;
+        default: cbPixelDest = 0;
+    }
+
+    switch (psoSrc->iBitmapFormat)
+    {
+        case BMF_16BPP: cbPixelSrc = 2; break;
+        case BMF_24BPP: cbPixelSrc = 3; break;
+        case BMF_32BPP: cbPixelSrc = 4; break;
+        default: cbPixelSrc = 0;
+    }
+
+    if (cbPixelDest == 0 || cbPixelSrc == 0)
+    {
+        DISPDBG((1, "vbvaFindChangedRect: unsupported pixel format src %d dst %d\n", psoDest->iBitmapFormat, psoSrc->iBitmapFormat));
+        return TRUE;
+    }
+    
+    rclDest = *prclDest;
+    
+    vrdpAdjustRect (psoDest, &rclDest);
+    
+    pptlSrc->x += rclDest.left - prclDest->left;
+    pptlSrc->y += rclDest.top - prclDest->top;
+    
+    *prclDest = rclDest;
+    
+    if (   rclDest.right == rclDest.left
+        || rclDest.bottom == rclDest.top)
+    {
+        DISPDBG((1, "vbvaFindChangedRect: empty dest rect: %d-%d, %d-%d\n", rclDest.left, rclDest.right, rclDest.top, rclDest.bottom));
+        return FALSE;
+    }
+    
+    rclSrc.left   = pptlSrc->x;
+    rclSrc.top    = pptlSrc->y;
+    rclSrc.right  = pptlSrc->x + (rclDest.right - rclDest.left);
+    rclSrc.bottom = pptlSrc->y + (rclDest.bottom - rclDest.top);
+    vrdpAdjustRect (psoSrc, &rclSrc);
+    
+    if (   rclSrc.right == rclSrc.left
+        || rclSrc.bottom == rclSrc.top)
+    {
+         prclDest->right = prclDest->left;
+         prclDest->bottom = prclDest->top;
+         
+         DISPDBG((1, "vbvaFindChangedRect: empty src rect: %d-%d, %d-%d\n", rclSrc.left, rclSrc.right, rclSrc.top, rclSrc.bottom));
+         return FALSE;
+    }
+    
+    VBVA_ASSERT(pptlSrc->x == rclSrc.left);
+    VBVA_ASSERT(pptlSrc->y == rclSrc.top);
+    
+    /*
+     * Compare the content of the screen surface (psoDest) with the source surface (psoSrc).
+     * Update the prclDest with the rectangle that will be actually changed after
+     * copying the source bits to the screen.
+     */
+    pu8Src = (BYTE *)psoSrc->pvScan0 + psoSrc->lDelta * pptlSrc->y + cbPixelSrc * pptlSrc->x;
+    pu8Dest = (BYTE *)psoDest->pvScan0 + psoDest->lDelta * prclDest->top + cbPixelDest * prclDest->left;
+    
+    /* Use the rclDest as the bounding rectangle for the changed area. */
+    rclDest.left   = prclDest->right;  /* +inf */
+    rclDest.right  = prclDest->left;   /* -inf */
+    rclDest.top    = prclDest->bottom; /* +inf */
+    rclDest.bottom = prclDest->top;    /* -inf */
+    
+    fTopNonEqualFound = 0;
+    yTopmost = prclDest->top;        /* inclusive */
+    yBottommost = prclDest->top - 1; /* inclusive */
+    
+    for (y = prclDest->top; y < prclDest->bottom; y++)
+    {
+        int fLeftNonEqualFound = 0;
+        
+        /* Init to an empty line. */
+        int xLeftmost = prclDest->left;      /* inclusive */
+        int xRightmost = prclDest->left - 1; /* inclusive */
+        
+        BYTE *pu8SrcLine = pu8Src;
+        BYTE *pu8DestLine = pu8Dest;
+        
+        for (x = prclDest->left; x < prclDest->right; x++)
+        {
+            int fEqualPixels;
+            
+            if (cbPixelSrc == cbPixelDest)
+            {
+                fEqualPixels = (memcmp (pu8SrcLine, pu8DestLine, cbPixelDest) == 0);
+            }
+            else
+            {
+                /* Convert larger pixel to the smaller pixel format. */
+                ULONG ulConvertedPixel;
+                if (cbPixelSrc > cbPixelDest)
+                {
+                    /* Convert the source pixel to the destination pixel format. */
+                    ulConvertedPixel = vbvaConvertPixel ( /*from*/ pu8SrcLine, cbPixelSrc,
+                                                          /*to*/ cbPixelDest);
+                    fEqualPixels = (memcmp (&ulConvertedPixel, pu8DestLine, cbPixelDest) == 0);
+                }
+                else
+                {
+                    /* Convert the destination pixel to the source pixel format. */
+                    ulConvertedPixel = vbvaConvertPixel ( /*from*/ pu8DestLine, cbPixelDest,
+                                                          /*to*/ cbPixelSrc);
+                    fEqualPixels = (memcmp (&ulConvertedPixel, pu8SrcLine, cbPixelSrc) == 0);
+                }
+            }
+            
+            if (fEqualPixels)
+            {
+                /* Equal pixels. */
+                if (!fLeftNonEqualFound)
+                {
+                    xLeftmost = x;
+                }
+            }
+            else
+            {
+                fLeftNonEqualFound = 1;
+                xRightmost = x;
+            }
+            
+            pu8SrcLine += cbPixelSrc;
+            pu8DestLine += cbPixelDest;
+        }
+        
+        /* min */
+        if (rclDest.left > xLeftmost)
+        {
+            rclDest.left = xLeftmost;
+        }
+        
+        /* max */
+        if (rclDest.right < xRightmost)
+        {
+            rclDest.right = xRightmost;
+        }
+        
+        if (xLeftmost > xRightmost) /* xRightmost is inclusive, so '>', not '>='. */
+        {
+            /* Empty line. */
+            if (!fTopNonEqualFound)
+            {
+                yTopmost = y;
+            }
+        }
+        else
+        {
+            fTopNonEqualFound = 1;
+            yBottommost = y;
+        }
+        
+        pu8Src += psoSrc->lDelta;
+        pu8Dest += psoDest->lDelta;
+    }
+    
+    /* min */
+    if (rclDest.top > yTopmost)
+    {
+        rclDest.top = yTopmost;
+    }
+        
+    /* max */
+    if (rclDest.bottom < yBottommost)
+    {
+        rclDest.bottom = yBottommost;
+    }
+        
+    /* rclDest was calculated with right-bottom inclusive.
+     * The following checks and the caller require exclusive coords.
+     */
+    rclDest.right++;
+    rclDest.bottom++;
+    
+    DISPDBG((1, "vbvaFindChangedRect: new dest %d,%d %dx%d from %d,%d\n",
+             rclDest.left, rclDest.top, rclDest.right - rclDest.left, rclDest.bottom - rclDest.top,
+             pptlSrc->x, pptlSrc->y
+            ));
+            
+    /* Update the rectangle with the changed area. */
+    if (   rclDest.left >= rclDest.right
+        || rclDest.top >= rclDest.bottom)
+    {
+        /* Empty rect. */
+        DISPDBG((1, "vbvaFindChangedRect: empty\n"));
+        prclDest->right = prclDest->left;
+        prclDest->bottom = prclDest->top;
+        return FALSE;
+    }
+    
+    DISPDBG((1, "vbvaFindChangedRect: not empty\n"));
+    
+    pptlSrc->x += rclDest.left - prclDest->left;
+    pptlSrc->y += rclDest.top - prclDest->top;
+    
+    *prclDest = rclDest;
+    
+    return TRUE;
+}
+#endif /* VBOX_VBVA_ADJUST_RECT */
+
 void vboxReportDirtyRect (PPDEV ppdev, RECTL *pRectOrig)
 {
@@ -99,5 +381,5 @@
     PPDEV ppdev = (PPDEV)psoTrg->dhpdev;
 
-    vbvaReportDirtyRect (ppdev, prclTrg);
+    vbvaReportDirtyClip (ppdev, pco, prclTrg);
 }
 
Index: trunk/src/VBox/Additions/WINNT/Graphics/Display/vrdp.c
===================================================================
--- trunk/src/VBox/Additions/WINNT/Graphics/Display/vrdp.c	(revision 4150)
+++ trunk/src/VBox/Additions/WINNT/Graphics/Display/vrdp.c	(revision 4181)
@@ -153,5 +153,5 @@
     if (prcl->top > prcl->bottom)
     {
-        DISPDBG((1, "vrdpAdjustRect: Inverse Y coordinates!!!\n"));
+        DISPDBG((1, "vrdpOrderRect: Inverse Y coordinates!!!\n"));
 
         tmp = prcl->top;
@@ -162,5 +162,5 @@
 
 
-static void vrdpAdjustRect (SURFOBJ *pso, RECTL *prcl)
+void vrdpAdjustRect (SURFOBJ *pso, RECTL *prcl)
 {
     int x;
