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;