From bed91e67a016c04af890b3a3996adfd09ea2f6db Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 26 Mar 2022 00:19:09 +0800 Subject: [PATCH] Handle device orientation with vulkan pre-rotation --- .../include/ge_vulkan_driver.hpp | 6 ++ .../src/ge_vulkan_2d_renderer.cpp | 6 ++ lib/graphics_engine/src/ge_vulkan_driver.cpp | 90 +++++++++++++++++-- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/lib/graphics_engine/include/ge_vulkan_driver.hpp b/lib/graphics_engine/include/ge_vulkan_driver.hpp index cb5c1e08e..3d40aae98 100644 --- a/lib/graphics_engine/include/ge_vulkan_driver.hpp +++ b/lib/graphics_engine/include/ge_vulkan_driver.hpp @@ -314,6 +314,10 @@ namespace GE video::ITexture* getWhiteTexture() const { return m_white_texture; } video::ITexture* getTransparentTexture() const { return m_transparent_texture; } + void getRotatedRect2D(VkRect2D* rect); + void getRotatedViewport(VkViewport* vp); + const core::matrix4& getPreRotationMatrix() + { return m_pre_rotation_matrix; } private: struct SwapChainSupportDetails { @@ -429,6 +433,7 @@ namespace GE video::SColor m_clear_color; core::rect m_clip; core::rect m_viewport; + core::matrix4 m_pre_rotation_matrix; video::ITexture* m_white_texture; video::ITexture* m_transparent_texture; @@ -450,6 +455,7 @@ namespace GE void createRenderPass(); void createFramebuffers(); void createUnicolorTextures(); + void initPreRotationMatrix(); std::string getVulkanVersionString() const; std::string getDriverVersionString() const; }; diff --git a/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp b/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp index 32289676e..726991572 100644 --- a/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp +++ b/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp @@ -473,6 +473,7 @@ void GEVulkan2dRenderer::render() vp.height = g_vk->getViewPort().getHeight(); vp.minDepth = 0; vp.maxDepth = 1.0f; + g_vk->getRotatedViewport(&vp); vkCmdSetViewport(g_vk->getCurrentCommandBuffer(), 0, 1, &vp); if (GEVulkanFeatures::supportsBindTexturesAtOnce()) @@ -506,6 +507,7 @@ void GEVulkan2dRenderer::render() scissor.offset.y = clip.UpperLeftCorner.Y; scissor.extent.width = clip.getWidth(); scissor.extent.height = clip.getHeight(); + g_vk->getRotatedRect2D(&scissor); vkCmdSetScissor(g_vk->getCurrentCommandBuffer(), 0, 1, &scissor); vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1, @@ -532,6 +534,7 @@ void GEVulkan2dRenderer::render() scissor.offset.y = clip.UpperLeftCorner.Y; scissor.extent.width = clip.getWidth(); scissor.extent.height = clip.getHeight(); + g_vk->getRotatedRect2D(&scissor); vkCmdSetScissor(g_vk->getCurrentCommandBuffer(), 0, 1, &scissor); vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1, @@ -576,6 +579,9 @@ void GEVulkan2dRenderer::addVerticesIndices(irr::video::S3DVertex* vertices, vertex.Pos.Y / g_vk->getCurrentRenderTargetSize().Height); t.pos = t.pos * 2.0f; t.pos -= 1.0f; + core::vector3df position = core::vector3df(t.pos.X, t.pos.Y, 0); + g_vk->getPreRotationMatrix().transformVect(position); + t.pos = core::vector2df(position.X, position.Y); t.color = vertex.Color; t.uv = vertex.TCoords; t.sampler_idx = sampler_idx; diff --git a/lib/graphics_engine/src/ge_vulkan_driver.cpp b/lib/graphics_engine/src/ge_vulkan_driver.cpp index c37014800..0fb42fb8f 100644 --- a/lib/graphics_engine/src/ge_vulkan_driver.cpp +++ b/lib/graphics_engine/src/ge_vulkan_driver.cpp @@ -461,6 +461,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, m_clear_color = video::SColor(0); m_white_texture = NULL; m_transparent_texture = NULL; + m_pre_rotation_matrix = core::matrix4(core::matrix4::EM4CONST_IDENTITY); createInstance(window); @@ -970,12 +971,25 @@ found_mode: create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; } - create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + create_info.preTransform = m_surface_capabilities.currentTransform; create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; create_info.presentMode = present_mode; create_info.clipped = VK_TRUE; create_info.oldSwapchain = VK_NULL_HANDLE; + m_swap_chain_extent = image_extent; + ScreenSize.Width = m_swap_chain_extent.width; + ScreenSize.Height = m_swap_chain_extent.height; + m_clip = getFullscreenClip(); + setViewPort(core::recti(0, 0, ScreenSize.Width, ScreenSize.Height)); + initPreRotationMatrix(); + if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) != 0 || + (m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) != 0) + { + std::swap(create_info.imageExtent.width, create_info.imageExtent.height); + std::swap(m_swap_chain_extent.width, m_swap_chain_extent.height); + } + VkResult result = vkCreateSwapchainKHR(m_vk->device, &create_info, NULL, &m_vk->swap_chain); @@ -988,12 +1002,6 @@ found_mode: &m_vk->swap_chain_images[0]); m_swap_chain_image_format = surface_format.format; - m_swap_chain_extent = image_extent; - ScreenSize.Width = m_swap_chain_extent.width; - ScreenSize.Height = m_swap_chain_extent.height; - m_clip = getFullscreenClip(); - setViewPort(core::recti(0, 0, ScreenSize.Width, ScreenSize.Height)); - for (unsigned int i = 0; i < m_vk->swap_chain_images.size(); i++) { VkImageViewCreateInfo create_info = {}; @@ -1749,7 +1757,73 @@ void GEVulkanDriver::setViewPort(const core::rect& area) vp.clipAgainst(rendert); if (vp.getHeight() > 0 && vp.getWidth() > 0) m_viewport = vp; -} +} // setViewPort + +// ---------------------------------------------------------------------------- +void GEVulkanDriver::getRotatedRect2D(VkRect2D* rect) +{ + // https://developer.android.com/games/optimize/vulkan-prerotation + if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) != 0) + { + VkRect2D ret = + { + (int)(m_swap_chain_extent.width - rect->extent.height - rect->offset.y), + rect->offset.x, + rect->extent.height, + rect->extent.width + }; + *rect = ret; + } + else if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR) != 0) + { + VkRect2D ret = + { + (int)(m_swap_chain_extent.width - rect->extent.width - rect->offset.x), + (int)(m_swap_chain_extent.height - rect->extent.height - rect->offset.y), + rect->extent.width, + rect->extent.height + }; + *rect = ret; + } + else if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) != 0) + { + VkRect2D ret = + { + rect->offset.y, + (int)(m_swap_chain_extent.height - rect->extent.width - rect->offset.x), + rect->extent.height, + rect->extent.width + }; + *rect = ret; + } +} // getRotatedRect2D + +// ---------------------------------------------------------------------------- +void GEVulkanDriver::getRotatedViewport(VkViewport* vp) +{ + VkRect2D rect; + rect.offset.x = vp->x; + rect.offset.y = vp->y; + rect.extent.width = vp->width; + rect.extent.height = vp->height; + getRotatedRect2D(&rect); + vp->x = rect.offset.x; + vp->y = rect.offset.y; + vp->width = rect.extent.width; + vp->height = rect.extent.height; +} // getRotatedViewport + +// ---------------------------------------------------------------------------- +void GEVulkanDriver::initPreRotationMatrix() +{ + const double pi = 3.14159265358979323846; + if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) != 0) + m_pre_rotation_matrix.setRotationAxisRadians(90.0 * pi / 180., core::vector3df(0.0f, 0.0f, 1.0f)); + else if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR) != 0) + m_pre_rotation_matrix.setRotationAxisRadians(180.0 * pi / 180., core::vector3df(0.0f, 0.0f, 1.0f)); + else if ((m_surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) != 0) + m_pre_rotation_matrix.setRotationAxisRadians(270.0 * pi / 180., core::vector3df(0.0f, 0.0f, 1.0f)); +} // initPreRotationMatrix }