diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 0f3fdaeaba..54cb84ca14 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -929,6 +929,7 @@ bool VulkanContext::InitSwapchain() { if (physicalDeviceProperties_[physical_device_].properties.vendorID == VULKAN_VENDOR_IMGTEC) { // Swap chain width hack to avoid issue #11743 (PowerVR driver bug). + // TODO: Check if still broken if pretransform is used swapChainExtent_.width &= ~31; } @@ -975,17 +976,46 @@ bool VulkanContext::InitSwapchain() { // Application must settle for fewer images than desired: desiredNumberOfSwapChainImages = surfCapabilities_.maxImageCount; } - + + // We mostly follow the practices from + // https://arm-software.github.io/vulkan_best_practice_for_mobile_developers/samples/surface_rotation/surface_rotation_tutorial.html + // VkSurfaceTransformFlagBitsKHR preTransform; std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms); std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform); ILOG("Supported transforms: %s", supportedTransforms.c_str()); ILOG("Current transform: %s", currentTransform.c_str()); - if (surfCapabilities_.supportedTransforms & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) { + g_display_rotation = DisplayRotation::ROTATE_0; + g_display_rot_matrix.setIdentity(); + bool swapChainExtentSwap = false; + if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) { preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - } else { + } else if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) { + // Normal, sensible rotations. Let's handle it. preTransform = surfCapabilities_.currentTransform; + g_display_rot_matrix.setIdentity(); + switch (surfCapabilities_.currentTransform) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + g_display_rotation = DisplayRotation::ROTATE_90; + g_display_rot_matrix.setRotationZ90(); + std::swap(swapChainExtent_.width, swapChainExtent_.height); + break; + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + g_display_rotation = DisplayRotation::ROTATE_180; + g_display_rot_matrix.setRotationZ180(); + break; + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + g_display_rotation = DisplayRotation::ROTATE_270; + g_display_rot_matrix.setRotationZ270(); + std::swap(swapChainExtent_.width, swapChainExtent_.height); + break; + } + } else { + // Let the OS rotate the image (potentially slow on many Android devices) + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } + std::string preTransformStr = surface_transforms_to_string(preTransform); + ILOG("Chosen pretransform transform: %s", preTransformStr.c_str()); VkSwapchainCreateInfoKHR swap_chain_info{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; swap_chain_info.surface = surface_; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index fa667ec7bd..bc957da98c 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -20,6 +20,7 @@ #include "profiler/profiler.h" +#include "base/display.h" #include "base/timeutil.h" #include "math/lin/matrix4x4.h" #include "math/dataconv.h" @@ -302,6 +303,16 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa vtx[i].y = vtx[i].y * invDestH - 1.0f; } + if (g_display_rotation != DisplayRotation::ROTATE_0) { + for (int i = 0; i < 4; i++) { + // backwards notation, should fix that... + Vec3 v(vtx[i].x, vtx[i].y, 0.0f); + v = v * g_display_rot_matrix; + vtx[i].x = v.x; + vtx[i].y = v.y; + } + } + draw_->FlushState(); // TODO: Should probably use draw_ directly and not go low level diff --git a/ext/native/math/lin/matrix4x4.h b/ext/native/math/lin/matrix4x4.h index 3b52e4ecd7..db8acde2b9 100644 --- a/ext/native/math/lin/matrix4x4.h +++ b/ext/native/math/lin/matrix4x4.h @@ -111,6 +111,34 @@ public: zz = 1.0f; ww = 1.0f; } + // Exact angles to avoid any artifacts. + void setRotationZ90() { + empty(); + float c = 0.0f; + float s = 1.0f; + xx = c; xy = s; + yx = -s; yy = c; + zz = 1.0f; + ww = 1.0f; + } + void setRotationZ180() { + empty(); + float c = -1.0f; + float s = 0.0f; + xx = c; xy = s; + yx = -s; yy = c; + zz = 1.0f; + ww = 1.0f; + } + void setRotationZ270() { + empty(); + float c = 0.0f; + float s = -1.0f; + xx = c; xy = s; + yx = -s; yy = c; + zz = 1.0f; + ww = 1.0f; + } void setRotationAxisAngle(const Vec3 &axis, float angle); diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index e8ace0d6a4..de4d7250bd 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -3,6 +3,7 @@ #include #include "base/logging.h" +#include "base/display.h" #include "thin3d/thin3d.h" #include "Common/Log.h" #include "Common/ColorConv.h" @@ -352,6 +353,35 @@ DrawContext::~DrawContext() { DestroyPresets(); } +void DrawContext::RotateRectToDisplay(FRect &rect, float curRTWidth, float curRTHeight) { + if (g_display_rotation == DisplayRotation::ROTATE_0) + return; + switch (g_display_rotation) { + case DisplayRotation::ROTATE_180: + rect.x = curRTWidth - rect.w - rect.x; + rect.y = curRTHeight - rect.h - rect.y; + break; + case DisplayRotation::ROTATE_90: { + // Note that curRTWidth_ and curRTHeight_ are "swapped"! + float origX = rect.x; + float origY = rect.y; + float rtw = curRTHeight; + float rth = curRTWidth; + rect.x = rth - rect.h - origY; + rect.y = origX; + std::swap(rect.w, rect.h); + break; + } + case DisplayRotation::ROTATE_270: { + float origX = rect.x; + float origY = rect.y; + // TODO + std::swap(rect.w, rect.h); + break; + } + } +} + // TODO: SSE/NEON // Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index c07b6130b5..b7d717447f 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -661,6 +661,11 @@ public: virtual void FlushState() {} protected: + struct FRect { + float x, y, w, h; + }; + void RotateRectToDisplay(FRect &rect, float curRTWidth, float curRTHeight); + ShaderModule *vsPresets_[VS_MAX_PRESET]; ShaderModule *fsPresets_[FS_MAX_PRESET]; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 2d8fae57b3..f301e24e99 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -159,12 +159,7 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; private: - struct FRect { - float x, y, w, h; - }; - void ApplyCurrentState(); - void RotateRectToDisplay(FRect &rect); HWND hWnd_; ID3D11Device *device_; @@ -362,42 +357,12 @@ void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1 } } -void D3D11DrawContext::RotateRectToDisplay(FRect &rect) { - if (g_display_rotation == DisplayRotation::ROTATE_0) - return; - if (curRenderTargetView_ != bbRenderTargetView_) - return; // Only the backbuffer is actually rotated wrong! - switch (g_display_rotation) { - case DisplayRotation::ROTATE_180: - rect.x = curRTWidth_ - rect.w - rect.x; - rect.y = curRTHeight_ - rect.h - rect.y; - break; - case DisplayRotation::ROTATE_90: { - // Note that curRTWidth_ and curRTHeight_ are "swapped"! - float origX = rect.x; - float origY = rect.y; - float rtw = curRTHeight_; - float rth = curRTWidth_; - rect.x = rth - rect.h - origY; - rect.y = origX; - std::swap(rect.w, rect.h); - break; - } - case DisplayRotation::ROTATE_270: { - float origX = rect.x; - float origY = rect.y; - // TODO - std::swap(rect.w, rect.h); - break; - } - } -} - void D3D11DrawContext::SetViewports(int count, Viewport *viewports) { D3D11_VIEWPORT vp[4]; for (int i = 0; i < count; i++) { FRect rc{ viewports[i].TopLeftX , viewports[i].TopLeftY, viewports[i].Width, viewports[i].Height }; - RotateRectToDisplay(rc); + if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong! + RotateRectToDisplay(rc, curRTWidth_, curRTHeight_); vp[i].TopLeftX = rc.x; vp[i].TopLeftY = rc.y; vp[i].Width = rc.w; @@ -410,8 +375,8 @@ void D3D11DrawContext::SetViewports(int count, Viewport *viewports) { void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) { FRect frc{ (float)left, (float)top, (float)width, (float)height }; - RotateRectToDisplay(frc); - + if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong! + RotateRectToDisplay(frc, curRTWidth_, curRTHeight_); D3D11_RECT rc{}; rc.left = (INT)frc.x; rc.top = (INT)frc.y; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 1479a7367b..a62761feea 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1059,17 +1059,30 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } void VKContext::SetScissorRect(int left, int top, int width, int height) { - VkRect2D scissor{ {left, top}, {(uint32_t)width, (uint32_t)height} }; + FRect rc{ (float)left, (float)top, (float)width, (float)height }; + if (curFramebuffer_ == nullptr) { // Only the backbuffer is actually rotated wrong! + int curRTWidth, curRTHeight; + GetFramebufferDimensions((Framebuffer *)curFramebuffer_, &curRTWidth, &curRTHeight); + RotateRectToDisplay(rc, (float)curRTWidth, (float)curRTHeight); + } + VkRect2D scissor{ {(int32_t)rc.x, (int32_t)rc.y}, {(uint32_t)rc.w, (uint32_t)rc.h} }; renderManager_.SetScissor(scissor); } void VKContext::SetViewports(int count, Viewport *viewports) { if (count > 0) { + // Ignore viewports more than the first. VkViewport viewport; - viewport.x = viewports[0].TopLeftX; - viewport.y = viewports[0].TopLeftY; - viewport.width = viewports[0].Width; - viewport.height = viewports[0].Height; + FRect rc{ viewports[0].TopLeftX , viewports[0].TopLeftY, viewports[0].Width, viewports[0].Height }; + if (curFramebuffer_ == nullptr) { // Only the backbuffer is actually rotated wrong! + int curRTWidth, curRTHeight; + GetFramebufferDimensions((Framebuffer *)curFramebuffer_, &curRTWidth, &curRTHeight); + RotateRectToDisplay(rc, (float)curRTWidth, (float)curRTHeight); + } + viewport.x = rc.x; + viewport.y = rc.y; + viewport.width = rc.w; + viewport.height = rc.h; viewport.minDepth = viewports[0].MinDepth; viewport.maxDepth = viewports[0].MaxDepth; renderManager_.SetViewport(viewport);