From 6540c2eec1a1df0c66f7e08957b7aeb1dbcb31b0 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 21 Feb 2026 20:32:25 +0800 Subject: [PATCH] [Feat] (MG_Backend/DirectVulkan): properly handle Vulkan surface transform (pre-rotation) --- .../DirectVulkan/Renderer/ProgramFactory.cpp | 36 ++++++++++++++++--- .../DirectVulkan/Renderer/ProgramFactory.h | 3 ++ .../DirectVulkan/Renderer/SwapchainObject.cpp | 2 ++ .../DirectVulkan/Renderer/SwapchainObject.h | 2 ++ .../DirectVulkan/Renderer/VulkanRenderer.cpp | 17 +++++++-- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index db99322e..4a30f9ea 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -135,7 +135,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool InsertPositionFixup(spvtools::opt::IRContext* context, spvtools::opt::Instruction* insertBefore, - const PositionTargetInfo& target, Uint32 halfConstId, Bool doYFlip, Bool doZRemap) { + const PositionTargetInfo& target, Uint32 halfConstId, Bool doYFlip, Bool doZRemap, + Bool doSurfaceRotate90, Bool doSurfaceRotate180, Bool doSurfaceRotate270) { using namespace spvtools::opt; InstructionBuilder builder(context, insertBefore, IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); @@ -157,8 +158,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { auto* w = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {3}); if (!x || !y || !z || !w) return false; - if (!doYFlip && !doZRemap) return false; + if (!doYFlip && !doZRemap && !doSurfaceRotate90 && !doSurfaceRotate180 && !doSurfaceRotate270) { + return false; + } + Uint32 xValueId = x->result_id(); Uint32 yValueId = y->result_id(); if (doYFlip) { auto* negY = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, y->result_id()); @@ -166,6 +170,24 @@ namespace MobileGL::MG_Backend::DirectVulkan { yValueId = negY->result_id(); } + if (doSurfaceRotate90) { + auto* negY = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, yValueId); + if (!negY) return false; + xValueId = negY->result_id(); + yValueId = x->result_id(); + } else if (doSurfaceRotate180) { + auto* negX = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, xValueId); + auto* negY = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, yValueId); + if (!negX || !negY) return false; + xValueId = negX->result_id(); + yValueId = negY->result_id(); + } else if (doSurfaceRotate270) { + auto* negX = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, xValueId); + if (!negX) return false; + xValueId = yValueId; + yValueId = negX->result_id(); + } + Uint32 zValueId = z->result_id(); if (doZRemap) { auto* zPlusW = builder.AddBinaryOp(target.floatTypeId, spv::Op::OpFAdd, z->result_id(), w->result_id()); @@ -177,7 +199,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } auto* fixedPosition = builder.AddCompositeConstruct(target.vectorTypeId, - {x->result_id(), yValueId, zValueId, w->result_id()}); + {xValueId, yValueId, zValueId, w->result_id()}); if (!fixedPosition) return false; return builder.AddStore(positionPtrId, fixedPosition->result_id()) != nullptr; @@ -205,6 +227,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { const Bool doYFlip = (m_transformFlags & ProgramFactory::CompileOptionBit::PositionYFlip); const Bool doZRemap = (m_transformFlags & ProgramFactory::CompileOptionBit::PositionZRemap); + const Bool doSurfaceRotate90 = (m_transformFlags & ProgramFactory::CompileOptionBit::SurfaceRotate90); + const Bool doSurfaceRotate180 = + (m_transformFlags & ProgramFactory::CompileOptionBit::SurfaceRotate180); + const Bool doSurfaceRotate270 = + (m_transformFlags & ProgramFactory::CompileOptionBit::SurfaceRotate270); Bool modified = false; for (auto& entryPoint : get_module()->entry_points()) { @@ -228,7 +255,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { (model != spv::ExecutionModel::Geometry && inst->opcode() == spv::Op::OpReturn); if (!needsFixup) continue; - modified |= InsertPositionFixup(context(), inst, target, halfConstId, doYFlip, doZRemap); + modified |= InsertPositionFixup(context(), inst, target, halfConstId, doYFlip, doZRemap, + doSurfaceRotate90, doSurfaceRotate180, doSurfaceRotate270); } } } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h index 21f0ce5d..15ddc913 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h @@ -20,6 +20,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { None = 0, PositionYFlip = 1 << 0, PositionZRemap = 1 << 1, + SurfaceRotate90 = 1 << 2, + SurfaceRotate180 = 1 << 3, + SurfaceRotate270 = 1 << 4, }; using CompileOptionFlags = Flags; using HashType = Uint64; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp index f4bc8d74..c62df4eb 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp @@ -173,6 +173,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_surfaceFormat = {createInfo.imageFormat, createInfo.imageColorSpace}; m_extent = createInfo.imageExtent; + m_preTransform = createInfo.preTransform; VK_VERIFY(vkCreateSwapchainKHR(device, &createInfo, nullptr, &m_swapchain)); @@ -202,6 +203,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_images.clear(); m_imageLayouts.clear(); + m_preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; } VkImage SwapchainObject::GetImage(Uint32 index) const { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h index 649c94f2..30854d40 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h @@ -35,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkSwapchainKHR GetHandle() const { return m_swapchain; } const VkSurfaceFormatKHR& GetSurfaceFormat() const { return m_surfaceFormat; } VkExtent2D GetExtent() const { return m_extent; } + VkSurfaceTransformFlagBitsKHR GetPreTransform() const { return m_preTransform; } const Vector& GetImages() const { return m_images; } const Vector& GetImageViews() const { return m_imageViews; } VkImage GetImage(Uint32 index) const; @@ -54,6 +55,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkSwapchainKHR m_swapchain = VK_NULL_HANDLE; VkSurfaceFormatKHR m_surfaceFormat{}; VkExtent2D m_extent{}; + VkSurfaceTransformFlagBitsKHR m_preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; Vector m_images; Vector m_imageViews; Vector m_imageLayouts; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 417120e0..0db7ee3f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -62,12 +62,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { Shutdown(); } - inline ProgramFactory::CompileOptionFlags GetShaderTransformFlags() { + inline ProgramFactory::CompileOptionFlags GetShaderTransformFlags(VkSurfaceTransformFlagBitsKHR preTransform) { ProgramFactory::CompileOptionFlags flags = ProgramFactory::CompileOptionBit::PositionZRemap; const auto& currentDrawFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); if (currentDrawFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { flags |= ProgramFactory::CompileOptionBit::PositionYFlip; + switch (preTransform) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + flags |= ProgramFactory::CompileOptionBit::SurfaceRotate90; + break; + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + flags |= ProgramFactory::CompileOptionBit::SurfaceRotate180; + break; + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + flags |= ProgramFactory::CompileOptionBit::SurfaceRotate270; + break; + default: + break; + } } return flags; } @@ -77,7 +90,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const VkPipelineVertexInputStateCreateInfo& vertexInputState) { MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized"); MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized"); - ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(); + ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(program, transformFlags); if (stages.empty()) { MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages");