Fix Win32SurfaceKHR resize issues

* Fixes crash when window is resized.
* Take destination pitch into account when updating the framebuffer, so
we no longer get skewed renders.
* Return VK_ERROR_OUT_OF_DATE_KHR when presenting if the swapchain image
dimensions do not match the window surface's.

Bug: b/139372840
Change-Id: I744adbf222a1d06bc9cc879e299b11b56f6dd7cf
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/37429
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 7d4209d..7258e8f 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -733,6 +733,11 @@
 	device->getBlitter()->blit(this, dstImage, region, filter);
 }
 
+void Image::blitToBuffer(VkImageSubresourceLayers subresource, VkOffset3D offset, VkExtent3D extent, uint8_t* dst, int bufferRowPitch, int bufferSlicePitch) const
+{
+	device->getBlitter()->blitToBuffer(this, subresource, offset, extent, dst, bufferRowPitch, bufferSlicePitch);
+}
+
 void Image::resolve(Image* dstImage, const VkImageResolve& region) const
 {
 	VkImageBlit blitRegion;
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index d56fd05..cf16794 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -41,6 +41,7 @@
 	void copyFrom(Buffer* srcBuffer, const VkBufferImageCopy& region);
 
 	void blit(Image* dstImage, const VkImageBlit& region, VkFilter filter) const;
+	void blitToBuffer(VkImageSubresourceLayers subresource, VkOffset3D offset, VkExtent3D extent, uint8_t* dst, int bufferRowPitch, int bufferSlicePitch) const;
 	void resolve(Image* dstImage, const VkImageResolve& region) const;
 	void clear(const VkClearValue& clearValue, const vk::Format& viewFormat, const VkRect2D& renderArea, const VkImageSubresourceRange& subresourceRange);
 	void clear(const VkClearColorValue& color, const VkImageSubresourceRange& subresourceRange);
diff --git a/src/Vulkan/VkQueue.cpp b/src/Vulkan/VkQueue.cpp
index 8e41bbb..c38b42c 100644
--- a/src/Vulkan/VkQueue.cpp
+++ b/src/Vulkan/VkQueue.cpp
@@ -213,7 +213,7 @@
 }
 
 #ifndef __ANDROID__
-void Queue::present(const VkPresentInfoKHR* presentInfo)
+VkResult Queue::present(const VkPresentInfoKHR* presentInfo)
 {
 	// This is a hack to deal with screen tearing for now.
 	// Need to correctly implement threading using VkSemaphore
@@ -227,8 +227,12 @@
 
 	for(uint32_t i = 0; i < presentInfo->swapchainCount; i++)
 	{
-		vk::Cast(presentInfo->pSwapchains[i])->present(presentInfo->pImageIndices[i]);
+		VkResult result = vk::Cast(presentInfo->pSwapchains[i])->present(presentInfo->pImageIndices[i]);
+		if (result != VK_SUCCESS)
+			return result;
 	}
+
+	return VK_SUCCESS;
 }
 #endif
 
diff --git a/src/Vulkan/VkQueue.hpp b/src/Vulkan/VkQueue.hpp
index 0bf3d00..cf1f955 100644
--- a/src/Vulkan/VkQueue.hpp
+++ b/src/Vulkan/VkQueue.hpp
@@ -55,7 +55,7 @@
 	VkResult submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, Fence* fence);
 	VkResult waitIdle();
 #ifndef __ANDROID__
-	void present(const VkPresentInfoKHR* presentInfo);
+	VkResult present(const VkPresentInfoKHR* presentInfo);
 #endif
 
 private:
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 320ec99..77d38b3 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -2927,9 +2927,7 @@
 	TRACE("(VkQueue queue = %p, const VkPresentInfoKHR* pPresentInfo = %p)",
 			queue, pPresentInfo);
 
-	vk::Cast(queue)->present(pPresentInfo);
-
-	return VK_SUCCESS;
+	return vk::Cast(queue)->present(pPresentInfo);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR *pAcquireInfo, uint32_t *pImageIndex)
diff --git a/src/WSI/MacOSSurfaceMVK.h b/src/WSI/MacOSSurfaceMVK.h
index 7822fb6..a9950d9 100644
--- a/src/WSI/MacOSSurfaceMVK.h
+++ b/src/WSI/MacOSSurfaceMVK.h
@@ -35,7 +35,7 @@
 
     virtual void attachImage(PresentImage* image) override {}
     virtual void detachImage(PresentImage* image) override {}
-    void present(PresentImage* image) override;
+    VkResult present(PresentImage* image) override;
 
 private:
     MetalLayer* metalLayer = nullptr;
diff --git a/src/WSI/MacOSSurfaceMVK.mm b/src/WSI/MacOSSurfaceMVK.mm
index 7db42c1..2a80956 100644
--- a/src/WSI/MacOSSurfaceMVK.mm
+++ b/src/WSI/MacOSSurfaceMVK.mm
@@ -128,7 +128,7 @@
     pSurfaceCapabilities->maxImageExtent = extent;
 }
 
-void MacOSSurfaceMVK::present(PresentImage* image)
+VkResult MacOSSurfaceMVK::present(PresentImage* image)
 {
     auto drawable = metalLayer->getNextDrawable();
     if(drawable)
@@ -140,6 +140,7 @@
                           bytesPerRow:image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0)];
         [drawable present];
     }
+    return VK_SUCCESS;
 }
 
 }
diff --git a/src/WSI/VkSurfaceKHR.hpp b/src/WSI/VkSurfaceKHR.hpp
index 0e833da..f14d98b 100644
--- a/src/WSI/VkSurfaceKHR.hpp
+++ b/src/WSI/VkSurfaceKHR.hpp
@@ -90,7 +90,7 @@
 
 	virtual void attachImage(PresentImage* image) = 0;
 	virtual void detachImage(PresentImage* image) = 0;
-	virtual void present(PresentImage* image) = 0;
+	virtual VkResult present(PresentImage* image) = 0;
 
 	void associateSwapchain(SwapchainKHR* swapchain);
 	void disassociateSwapchain();
diff --git a/src/WSI/VkSwapchainKHR.cpp b/src/WSI/VkSwapchainKHR.cpp
index b038f20..ba97ebc 100644
--- a/src/WSI/VkSwapchainKHR.cpp
+++ b/src/WSI/VkSwapchainKHR.cpp
@@ -198,11 +198,11 @@
 	return VK_NOT_READY;
 }
 
-void SwapchainKHR::present(uint32_t index)
+VkResult SwapchainKHR::present(uint32_t index)
 {
-	auto & image = images[index];
+	auto& image = images[index];
 	image.setStatus(PRESENTING);
-	surface->present(&image);
+	VkResult result = surface->present(&image);
 	image.setStatus(AVAILABLE);
 
 	if(retired)
@@ -210,6 +210,8 @@
 		surface->detachImage(&image);
 		image.clear();
 	}
+
+	return result;
 }
 
 }
\ No newline at end of file
diff --git a/src/WSI/VkSwapchainKHR.hpp b/src/WSI/VkSwapchainKHR.hpp
index 7a77ebb..1bfdb93 100644
--- a/src/WSI/VkSwapchainKHR.hpp
+++ b/src/WSI/VkSwapchainKHR.hpp
@@ -46,7 +46,7 @@
 
 	VkResult getNextImage(uint64_t timeout, Semaphore* semaphore, Fence* fence, uint32_t* pImageIndex);
 
-	void present(uint32_t index);
+	VkResult present(uint32_t index);
 	PresentImage const &getImage(uint32_t imageIndex) { return images[imageIndex]; }
 
 private:
diff --git a/src/WSI/Win32SurfaceKHR.cpp b/src/WSI/Win32SurfaceKHR.cpp
index f9a8c24..05e6068 100644
--- a/src/WSI/Win32SurfaceKHR.cpp
+++ b/src/WSI/Win32SurfaceKHR.cpp
@@ -19,37 +19,35 @@
 
 #include <string.h>
 
+namespace
+{
+	VkExtent2D getWindowSize(HWND hwnd)
+	{
+		RECT clientRect = {};
+		BOOL status = GetClientRect(hwnd, &clientRect);
+		ASSERT(status != 0);
+
+		int windowWidth = clientRect.right - clientRect.left;
+		int windowHeight = clientRect.bottom - clientRect.top;
+
+		return { static_cast<uint32_t>(windowWidth), static_cast<uint32_t>(windowHeight) };
+	}
+}
+
 namespace vk {
 
 Win32SurfaceKHR::Win32SurfaceKHR(const VkWin32SurfaceCreateInfoKHR *pCreateInfo, void *mem) :
 		hwnd(pCreateInfo->hwnd)
 {
 	ASSERT(IsWindow(hwnd) == TRUE);
-
-	RECT clientRect = {};
-	BOOL status = GetClientRect(hwnd, &clientRect);
-	ASSERT(status != 0);
-
 	windowContext = GetDC(hwnd);
 	bitmapContext = CreateCompatibleDC(windowContext);
-
-	BITMAPINFO bitmapInfo = {};
-	bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO);
-	bitmapInfo.bmiHeader.biBitCount = 32;
-	bitmapInfo.bmiHeader.biPlanes = 1;
-	bitmapInfo.bmiHeader.biHeight = clientRect.top - clientRect.bottom;
-	bitmapInfo.bmiHeader.biWidth = clientRect.right - clientRect.left;
-	bitmapInfo.bmiHeader.biCompression = BI_RGB;
-
-	bitmap = CreateDIBSection(bitmapContext, &bitmapInfo, DIB_RGB_COLORS, &framebuffer, 0, 0);
-	ASSERT(bitmap != NULL);
-	SelectObject(bitmapContext, bitmap);
+	lazyCreateFrameBuffer();
 }
 
 void Win32SurfaceKHR::destroySurface(const VkAllocationCallbacks *pAllocator)
 {
-	SelectObject(bitmapContext, NULL);
-	DeleteObject(bitmap);
+	destroyFrameBuffer();
 	ReleaseDC(hwnd, windowContext);
 	DeleteDC(bitmapContext);
 }
@@ -62,15 +60,7 @@
 void Win32SurfaceKHR::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const
 {
 	SurfaceKHR::getSurfaceCapabilities(pSurfaceCapabilities);
-
-	RECT clientRect = {};
-	BOOL status = GetClientRect(hwnd, &clientRect);
-	ASSERT(status != 0);
-
-	int windowWidth = clientRect.right - clientRect.left;
-	int windowHeight = clientRect.bottom - clientRect.top;
-
-	VkExtent2D extent = {static_cast<uint32_t>(windowWidth), static_cast<uint32_t>(windowHeight)};
+	VkExtent2D extent = getWindowSize(hwnd);
 	pSurfaceCapabilities->currentExtent = extent;
 	pSurfaceCapabilities->minImageExtent = extent;
 	pSurfaceCapabilities->maxImageExtent = extent;
@@ -88,20 +78,80 @@
 	// present instead of associating the image with the surface.
 }
 
-void Win32SurfaceKHR::present(PresentImage* image)
+VkResult Win32SurfaceKHR::present(PresentImage* image)
 {
+	// Recreate frame buffer in case window size has changed
+	lazyCreateFrameBuffer();
+
 	if(!framebuffer)
 	{
-		return;
+		// e.g. window width or height is 0
+		return VK_SUCCESS;
 	}
 
 	VkExtent3D extent = image->getImage()->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0);
 
-	// FIXME(b/139184291): Assumes B8G8R8A8 surface without stride padding.
-	size_t bytes = extent.width * extent.height * 4;
-	memcpy(framebuffer, image->getImageMemory()->getOffsetPointer(0), bytes);
+	if (windowExtent.width != extent.width || windowExtent.height != extent.height)
+	{
+		return VK_ERROR_OUT_OF_DATE_KHR;
+	}
+
+	VkImageSubresourceLayers subresourceLayers{};
+	subresourceLayers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+	subresourceLayers.layerCount = 1;
+
+	image->getImage()->blitToBuffer(subresourceLayers, VkOffset3D{}, extent, reinterpret_cast<uint8_t*>(framebuffer), bitmapRowPitch, 0);
 
 	StretchBlt(windowContext, 0, 0, extent.width, extent.height, bitmapContext, 0, 0, extent.width, extent.height, SRCCOPY);
+
+	return VK_SUCCESS;
+}
+
+void Win32SurfaceKHR::lazyCreateFrameBuffer()
+{
+	auto currWindowExtent = getWindowSize(hwnd);
+	if (currWindowExtent.width == windowExtent.width && currWindowExtent.height == windowExtent.height)
+	{
+		return;
+	}
+
+	windowExtent = currWindowExtent;
+
+	if (framebuffer)
+	{
+		destroyFrameBuffer();
+	}
+
+	if (windowExtent.width == 0 || windowExtent.height == 0)
+	{
+		return;
+	}
+
+	BITMAPINFO bitmapInfo = {};
+	bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFO);
+	bitmapInfo.bmiHeader.biBitCount = 32;
+	bitmapInfo.bmiHeader.biPlanes = 1;
+	bitmapInfo.bmiHeader.biHeight = -windowExtent.height;
+	bitmapInfo.bmiHeader.biWidth = windowExtent.width;
+	bitmapInfo.bmiHeader.biCompression = BI_RGB;
+
+	bitmap = CreateDIBSection(bitmapContext, &bitmapInfo, DIB_RGB_COLORS, &framebuffer, 0, 0);
+	ASSERT(bitmap != NULL);
+	SelectObject(bitmapContext, bitmap);
+
+	BITMAP header;
+	int status = GetObject(bitmap, sizeof(BITMAP), &header);
+	ASSERT(status != 0);
+	bitmapRowPitch = static_cast<int>(header.bmWidthBytes);
+}
+
+void Win32SurfaceKHR::destroyFrameBuffer()
+{
+	SelectObject(bitmapContext, NULL);
+	DeleteObject(bitmap);
+	bitmap = {};
+	bitmapRowPitch = 0;
+	framebuffer = nullptr;
 }
 
 }
\ No newline at end of file
diff --git a/src/WSI/Win32SurfaceKHR.hpp b/src/WSI/Win32SurfaceKHR.hpp
index fe7be2c..4ed0594 100644
--- a/src/WSI/Win32SurfaceKHR.hpp
+++ b/src/WSI/Win32SurfaceKHR.hpp
@@ -41,15 +41,20 @@
 
 	virtual void attachImage(PresentImage* image) override;
 	virtual void detachImage(PresentImage* image) override;
-	void present(PresentImage* image) override;
+	VkResult present(PresentImage* image) override;
 
 private:
+	void lazyCreateFrameBuffer();
+	void destroyFrameBuffer();
+
 	const HWND hwnd;
 
 	HDC windowContext = {};
 	HDC bitmapContext = {};
+	VkExtent2D windowExtent = {};
 
 	HBITMAP bitmap = {};
+	int bitmapRowPitch = 0;
 	void *framebuffer = nullptr;
 };
 
diff --git a/src/WSI/XcbSurfaceKHR.cpp b/src/WSI/XcbSurfaceKHR.cpp
index 81fe40e..fc1b039 100644
--- a/src/WSI/XcbSurfaceKHR.cpp
+++ b/src/WSI/XcbSurfaceKHR.cpp
@@ -139,7 +139,7 @@
 	}
 }
 
-void XcbSurfaceKHR::present(PresentImage* image)
+VkResult XcbSurfaceKHR::present(PresentImage* image)
 {
 	auto it = graphicsContexts.find(image);
 	if(it != graphicsContexts.end())
@@ -167,6 +167,8 @@
 
 		libXcb->xcb_flush(connection);
 	}
+
+	return VK_SUCCESS;
 }
 
 }
\ No newline at end of file
diff --git a/src/WSI/XcbSurfaceKHR.hpp b/src/WSI/XcbSurfaceKHR.hpp
index dacd382..e40615e 100644
--- a/src/WSI/XcbSurfaceKHR.hpp
+++ b/src/WSI/XcbSurfaceKHR.hpp
@@ -36,7 +36,7 @@
 
 	virtual void attachImage(PresentImage* image) override;
 	virtual void detachImage(PresentImage* image) override;
-	void present(PresentImage* image) override;
+	VkResult present(PresentImage* image) override;
 
 private:
 	xcb_connection_t* connection;
diff --git a/src/WSI/XlibSurfaceKHR.cpp b/src/WSI/XlibSurfaceKHR.cpp
index 0ebca02..d40088a 100644
--- a/src/WSI/XlibSurfaceKHR.cpp
+++ b/src/WSI/XlibSurfaceKHR.cpp
@@ -82,7 +82,7 @@
 	}
 }
 
-void XlibSurfaceKHR::present(PresentImage* image)
+VkResult XlibSurfaceKHR::present(PresentImage* image)
 {
 	auto it = imageMap.find(image);
 	if(it != imageMap.end())
@@ -95,6 +95,8 @@
 			libX11->XPutImage(pDisplay, window, gc, xImage, 0, 0, 0, 0, extent.width, extent.height);
 		}
 	}
+
+	return VK_SUCCESS;
 }
 
 }
\ No newline at end of file
diff --git a/src/WSI/XlibSurfaceKHR.hpp b/src/WSI/XlibSurfaceKHR.hpp
index f580836..4167b1b 100644
--- a/src/WSI/XlibSurfaceKHR.hpp
+++ b/src/WSI/XlibSurfaceKHR.hpp
@@ -36,7 +36,7 @@
 
 	virtual void attachImage(PresentImage* image) override;
 	virtual void detachImage(PresentImage* image) override;
-	void present(PresentImage* image) override;
+	VkResult present(PresentImage* image) override;
 
 private:
 	Display *const pDisplay;