Add support for preTransform in Vulkan swapchain.
Test: manual building and running of apps.
Bug: 110985606
Bug: 122662274
Change-Id: Id53aa1ce028b448b1f12558eec78a60e09512166
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 6c540f6..5c6cb9a 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -480,6 +480,32 @@
return backbuffer;
}
+static SkMatrix getPreTransformMatrix(int width, int height,
+ VkSurfaceTransformFlagBitsKHR transform) {
+ switch (transform) {
+ case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+ return SkMatrix::I();
+ case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+ return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
+ case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+ return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
+ case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+ return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
+ case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
+ return SkMatrix::MakeAll(-1, 0, width, 0, 1, 0, 0, 0, 1);
+ case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
+ return SkMatrix::MakeAll(0, -1, height, -1, 0, width, 0, 0, 1);
+ case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
+ return SkMatrix::MakeAll(1, 0, 0, 0, -1, height, 0, 0, 1);
+ case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
+ return SkMatrix::MakeAll(0, 1, 0, 1, 0, 0, 0, 0, 1);
+ default:
+ LOG_ALWAYS_FATAL("Unsupported pre transform of swapchain.");
+ }
+ return SkMatrix::I();
+}
+
+
SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) {
// Recreate VulkanSurface, if ANativeWindow has been resized.
VulkanSurface* surface = *surfaceOut;
@@ -516,7 +542,7 @@
// maybe use attach somehow? but need a Window
return nullptr;
}
- if (VK_ERROR_OUT_OF_DATE_KHR == res) {
+ if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_SUBOPTIMAL_KHR == res) {
// tear swapchain down and try again
if (!createSwapchain(surface)) {
return nullptr;
@@ -595,6 +621,10 @@
}
backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ surface->mPreTransform = getPreTransformMatrix(surface->windowWidth(),
+ surface->windowHeight(),
+ surface->mTransform);
+
surface->mBackbuffer = std::move(skSurface);
return surface->mBackbuffer.get();
}
@@ -749,6 +779,17 @@
return false;
}
+ if (!SkToBool(caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) {
+ return false;
+ }
+ VkSurfaceTransformFlagBitsKHR transform;
+ if (SkToBool(caps.supportedTransforms & caps.currentTransform) &&
+ !SkToBool(caps.currentTransform & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
+ transform = caps.currentTransform;
+ } else {
+ transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ }
+
VkExtent2D extent = caps.currentExtent;
// clamp width; to handle currentExtent of -1 and protect us from broken hints
if (extent.width < caps.minImageExtent.width) {
@@ -760,6 +801,16 @@
extent.height = caps.minImageExtent.height;
}
SkASSERT(extent.height <= caps.maxImageExtent.height);
+
+ VkExtent2D swapExtent = extent;
+ if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
+ transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR ||
+ transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR ||
+ transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) {
+ swapExtent.width = extent.height;
+ swapExtent.height = extent.width;
+ }
+
surface->mWindowWidth = extent.width;
surface->mWindowHeight = extent.height;
@@ -775,7 +826,7 @@
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
- SkASSERT(caps.supportedTransforms & caps.currentTransform);
+
SkASSERT(caps.supportedCompositeAlpha &
(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
VkCompositeAlphaFlagBitsKHR composite_alpha =
@@ -822,7 +873,7 @@
swapchainCreateInfo.minImageCount = imageCount;
swapchainCreateInfo.imageFormat = surfaceFormat;
swapchainCreateInfo.imageColorSpace = colorSpace;
- swapchainCreateInfo.imageExtent = extent;
+ swapchainCreateInfo.imageExtent = swapExtent;
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = usageFlags;
@@ -837,7 +888,7 @@
swapchainCreateInfo.pQueueFamilyIndices = nullptr;
}
- swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ swapchainCreateInfo.preTransform = transform;
swapchainCreateInfo.compositeAlpha = composite_alpha;
swapchainCreateInfo.presentMode = mode;
swapchainCreateInfo.clipped = true;
@@ -848,6 +899,8 @@
return false;
}
+ surface->mTransform = transform;
+
// destroy the old swapchain
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
mDeviceWaitIdle(mDevice);
@@ -857,7 +910,7 @@
mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
}
- createBuffers(surface, surfaceFormat, extent);
+ createBuffers(surface, surfaceFormat, swapExtent);
// The window content is not updated (frozen) until a buffer of the window size is received.
// This prevents temporary stretching of the window after it is resized, but before the first
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 105ee09..b06eb82 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -45,6 +45,14 @@
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
+ // The width and height are are the logical width and height for when submitting draws to the
+ // surface. In reality if the window is rotated the underlying VkImage may have the width and
+ // height swapped.
+ int windowWidth() const { return mWindowWidth; }
+ int windowHeight() const { return mWindowHeight; }
+
+ SkMatrix& preTransform() { return mPreTransform; }
+
private:
friend class VulkanManager;
struct BackbufferInfo {
@@ -84,6 +92,8 @@
sk_sp<SkColorSpace> mColorSpace;
SkColorSpace::Gamut mColorGamut;
SkColorType mColorType;
+ VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ SkMatrix mPreTransform;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,