diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index d150ed707..dc72ef882 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,3 +1,11 @@
+December 1, 2009
+- Added another surface to receive a copy of the top back buffer immediately
+  before it is presented. This effectively produces a copy of the front
+  buffer without the performance penalty of GetFrontBufferData, so fullscreen
+  wipe preparation and screenshots are faster now. At lower resolutions,
+  always copying the backbuffer does incur a slight FPS hit, but it's
+  practically free at higher resolutions.
+
 November 30, 2009
 - The initial wipe screen is now kept in video memory. I had previously
   assumed that since the wipes only run at 35 FPS, the time spent DMA'ing
diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp
index abcd952cb..9c34db2bd 100644
--- a/src/win32/fb_d3d9.cpp
+++ b/src/win32/fb_d3d9.cpp
@@ -253,6 +253,7 @@ D3DFB::D3DFB (int width, int height, bool fullscreen)
 	FinalWipeScreen = NULL;
 	PaletteTexture = NULL;
 	GammaTexture = NULL;
+	FrontCopySurface = NULL;
 	for (int i = 0; i < NUM_SHADERS; ++i)
 	{
 		Shaders[i] = NULL;
@@ -637,6 +638,7 @@ void D3DFB::ReleaseDefaultPoolItems()
 	SAFE_RELEASE( IndexBuffer );
 	SAFE_RELEASE( BlockSurface[0] );
 	SAFE_RELEASE( BlockSurface[1] );
+	SAFE_RELEASE( FrontCopySurface );
 }
 
 //==========================================================================
@@ -748,14 +750,14 @@ void D3DFB::KillNativeTexs()
 
 bool D3DFB::CreateFBTexture ()
 {
-	if (FAILED(D3DDevice->CreateTexture (Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL)))
+	if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL)))
 	{
 		int pow2width, pow2height, i;
 
 		for (i = 1; i < Width; i <<= 1) {} pow2width = i;
 		for (i = 1; i < Height; i <<= 1) {} pow2height = i;
 
-		if (FAILED(D3DDevice->CreateTexture (pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL)))
+		if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL)))
 		{
 			return false;
 		}
@@ -770,7 +772,7 @@ bool D3DFB::CreateFBTexture ()
 		FBWidth = Width;
 		FBHeight = Height;
 	}
-	if (FAILED(D3DDevice->CreateTexture (FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &TempRenderTexture, NULL)))
+	if (FAILED(D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &TempRenderTexture, NULL)))
 	{
 		TempRenderTexture = NULL;
 	}
@@ -784,6 +786,10 @@ bool D3DFB::CreateFBTexture ()
 			surf->Release();
 		}
 	}
+	if (FAILED(D3DDevice->CreateRenderTarget(Width, Height, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &FrontCopySurface, NULL)))
+	{
+		return false;
+	}
 	return true;
 }
 
@@ -1156,6 +1162,8 @@ void D3DFB::Flip()
 	DoWindowedGamma();
 	D3DDevice->EndScene();
 
+	CopyNextFrontBuffer();
+
 	// Attempt to counter input lag.
 	if (d3d_antilag && BlockSurface[0] != NULL)
 	{
@@ -1173,6 +1181,42 @@ void D3DFB::Flip()
 	InScene = false;
 }
 
+//==========================================================================
+//
+// D3DFB :: CopyNextFrontBuffer
+//
+// Duplicates the contents of the back buffer that will become the front
+// buffer upon Present into FrontCopySurface so that we can get the
+// contents of the display without wasting time in GetFrontBufferData().
+//
+//==========================================================================
+
+void D3DFB::CopyNextFrontBuffer()
+{
+	IDirect3DSurface9 *backbuff;
+
+	if (Windowed || PixelDoubling)
+	{
+		// Windowed mode or pixel doubling: TempRenderTexture has what we want
+		if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &backbuff)))
+		{
+			D3DDevice->StretchRect(backbuff, NULL, FrontCopySurface, NULL, D3DTEXF_NONE);
+			backbuff->Release();
+		}
+	}
+	else
+	{
+		// Fullscreen, not pixel doubled: The back buffer has what we want,
+		// but it might be letter boxed.
+		if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuff)))
+		{
+			RECT srcrect = { 0, LBOffsetI, Width, LBOffsetI + Height };
+			D3DDevice->StretchRect(backbuff, &srcrect, FrontCopySurface, NULL, D3DTEXF_NONE);
+			backbuff->Release();
+		}
+	}
+}
+
 //==========================================================================
 //
 // D3DFB :: PaintToWindow
@@ -1688,50 +1732,25 @@ void D3DFB::ReleaseScreenshotBuffer()
 IDirect3DTexture9 *D3DFB::GetCurrentScreen(D3DPOOL pool)
 {
 	IDirect3DTexture9 *tex;
-	IDirect3DSurface9 *tsurf, *surf;
+	IDirect3DSurface9 *surf;
 	D3DSURFACE_DESC desc;
+	HRESULT hr;
 
 	assert(pool == D3DPOOL_SYSTEMMEM || pool == D3DPOOL_DEFAULT);
 
-	if (Windowed || PixelDoubling)
+	if (FAILED(FrontCopySurface->GetDesc(&desc)))
 	{
-		// The texture we read into must have the same pixel format as
-		// the TempRenderTexture.
-		if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
-		{
-			if (FAILED(tsurf->GetDesc(&desc)))
-			{
-				tsurf->Release();
-				return NULL;
-			}
-			tsurf->Release();
-		}
-		else
-		{
-			return NULL;
-		}
+		return NULL;
+	}
+	if (pool == D3DPOOL_SYSTEMMEM)
+	{
+		hr = D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0, desc.Format, D3DPOOL_SYSTEMMEM, &tex, NULL);
 	}
 	else
 	{
-		if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &tsurf)))
-		{
-			if (FAILED(tsurf->GetDesc(&desc)))
-			{
-				tsurf->Release();
-				return NULL;
-			}
-			tsurf->Release();
-		}
-		else
-		{
-			return NULL;
-		}
-		// GetFrontBufferData works only with this format
-		desc.Format = D3DFMT_A8R8G8B8;
+		hr = D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, desc.Format, D3DPOOL_DEFAULT, &tex, NULL);
 	}
-
-	// Read the image data into system memory.
-	if (FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0, desc.Format, D3DPOOL_SYSTEMMEM, &tex, NULL)))
+	if (FAILED(hr))
 	{
 		return NULL;
 	}
@@ -1740,68 +1759,23 @@ IDirect3DTexture9 *D3DFB::GetCurrentScreen(D3DPOOL pool)
 		tex->Release();
 		return NULL;
 	}
-
-	if (!Windowed && !PixelDoubling)
+	if (pool == D3DPOOL_SYSTEMMEM)
 	{
-		if (FAILED(D3DDevice->GetFrontBufferData(0, surf)))
-		{
-			surf->Release();
-			tex->Release();
-			return NULL;
-		}
+		// Video -> System memory : use GetRenderTargetData
+		hr = D3DDevice->GetRenderTargetData(FrontCopySurface, surf);
 	}
 	else
 	{
-		if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf)))
-		{
-			if (FAILED(D3DDevice->GetRenderTargetData(tsurf, surf)))
-			{
-				tsurf->Release();
-				tex->Release();
-				return NULL;
-			}
-			tsurf->Release();
-		}
-		else
-		{
-			tex->Release();
-			return NULL;
-		}
-	}
-
-	// If the caller wants the screen in video memory, create a new texture, and copy back to that.
-	if (pool == D3DPOOL_DEFAULT)
-	{
-		IDirect3DTexture9 *vtex;
-		IDirect3DSurface9 *vsurf;
-
-		if (FAILED(D3DDevice->CreateTexture(FBWidth, FBHeight, 1, 0, desc.Format, D3DPOOL_DEFAULT, &vtex, NULL)))
-		{
-			surf->Release();
-			tex->Release();
-			return NULL;
-		}
-		if (FAILED(vtex->GetSurfaceLevel(0, &vsurf)))
-		{
-			vtex->Release();
-			surf->Release();
-			tex->Release();
-			return NULL;
-		}
-		if (FAILED(D3DDevice->UpdateSurface(surf, NULL, vsurf, NULL)))
-		{
-			vsurf->Release();
-			vtex->Release();
-			surf->Release();
-			tex->Release();
-			return NULL;
-		}
-		surf->Release();
-		tex->Release();
-		surf = vsurf;
-		tex = vtex;
+		// Video -> Video memory : use StretchRect
+		RECT destrect = { 0, 0, Width, Height };
+		hr = D3DDevice->StretchRect(FrontCopySurface, NULL, surf, &destrect, D3DTEXF_POINT);
 	}
 	surf->Release();
+	if (FAILED(hr))
+	{
+		tex->Release();
+		return NULL;
+	}
 	return tex;
 }
 
diff --git a/src/win32/fb_d3d9_wipe.cpp b/src/win32/fb_d3d9_wipe.cpp
index 8783a2588..798a95080 100644
--- a/src/win32/fb_d3d9_wipe.cpp
+++ b/src/win32/fb_d3d9_wipe.cpp
@@ -459,10 +459,9 @@ bool D3DFB::Wiper_Melt::Run(int ticks, D3DFB *fb)
 				dpt.x = i * fbwidth / WIDTH;
 				dpt.y = MAX(0, y[i] * fbheight / HEIGHT);
 				rect.left = dpt.x;
-				rect.top = fb->LBOffsetI;
+				rect.top = 0;
 				rect.right = (i + 1) * fbwidth / WIDTH;
-				rect.bottom = fbheight - dpt.y + fb->LBOffsetI;
-				dpt.y += fb->LBOffsetI;
+				rect.bottom = fbheight - dpt.y;
 				if (rect.bottom > rect.top)
 				{
 					fb->CheckQuadBatch();
@@ -485,8 +484,8 @@ bool D3DFB::Wiper_Melt::Run(int ticks, D3DFB *fb)
 
 					float x0 = float(rect.left) - 0.5f;
 					float x1 = float(rect.right) - 0.5f;
-					float y0 = float(dpt.y) - 0.5f;
-					float y1 = float(fbheight) - 0.5f;
+					float y0 = float(dpt.y + fb->LBOffsetI) - 0.5f;
+					float y1 = float(fbheight + fb->LBOffsetI) - 0.5f;
 
 					vert[0].x = x0;
 					vert[0].y = y0;
diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h
index 1684f9d82..e311049b7 100644
--- a/src/win32/win32iface.h
+++ b/src/win32/win32iface.h
@@ -363,6 +363,7 @@ private:
 	void BeginLineBatch();
 	void EndLineBatch();
 	void EndBatch();
+	void CopyNextFrontBuffer();
 
 	D3DCAPS9 DeviceCaps;
 
@@ -419,6 +420,7 @@ private:
 	IDirect3DTexture9 *GammaTexture;
 	IDirect3DTexture9 *ScreenshotTexture;
 	IDirect3DSurface9 *ScreenshotSurface;
+	IDirect3DSurface9 *FrontCopySurface;
 
 	IDirect3DVertexBuffer9 *VertexBuffer;
 	FBVERTEX *VertexData;