diff --git a/src/main/java/net/vulkanmod/vulkan/Renderer.java b/src/main/java/net/vulkanmod/vulkan/Renderer.java index f6598ab8d..2294fc1fe 100644 --- a/src/main/java/net/vulkanmod/vulkan/Renderer.java +++ b/src/main/java/net/vulkanmod/vulkan/Renderer.java @@ -31,6 +31,7 @@ import static org.lwjgl.system.MemoryStack.stackGet; import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.vulkan.EXTDebugUtils.*; +import static org.lwjgl.vulkan.KHRSurface.*; import static org.lwjgl.vulkan.KHRSwapchain.*; import static org.lwjgl.vulkan.VK10.*; @@ -543,7 +544,15 @@ public static void clearAttachments(int v, int width, int height) { public static void setViewport(int x, int y, int width, int height) { try(MemoryStack stack = stackPush()) { + VkExtent2D transformedExtent = transformToExtent(VkExtent2D.calloc(stack), width, height); + VkOffset2D transformedOffset = transformToOffset(VkOffset2D.calloc(stack), x, y, width, height); VkViewport.Buffer viewport = VkViewport.calloc(1, stack); + + x = transformedOffset.x(); + y = transformedOffset.y(); + width = transformedExtent.width(); + height = transformedExtent.height(); + viewport.x(x); viewport.y(height + y); viewport.width(width); @@ -553,20 +562,83 @@ public static void setViewport(int x, int y, int width, int height) { VkRect2D.Buffer scissor = VkRect2D.malloc(1, stack); scissor.offset(VkOffset2D.malloc(stack).set(0, 0)); - scissor.extent(VkExtent2D.malloc(stack).set(width, height)); + scissor.extent(transformedExtent); vkCmdSetViewport(INSTANCE.currentCmdBuffer, 0, viewport); vkCmdSetScissor(INSTANCE.currentCmdBuffer, 0, scissor); } } + /** + * Transform the X/Y coordinates from Minecraft coordinate space to Vulkan coordinate space + * and write them to VkOffset2D + * @param offset2D the offset to which the coordinates should be written + * @param x the X coordinate + * @param y the Y coordinate + * @param w the viewport/scissor operation width + * @param h the viewport/scissor operation height + * @return same offset2D with transformations applied as necessary + */ + private static VkOffset2D transformToOffset(VkOffset2D offset2D, int x, int y, int w, int h) { + int pretransformFlags = Vulkan.getPretransformFlags(); + if(pretransformFlags == 0) { + offset2D.set(x, y); + return offset2D; + } + Framebuffer boundFramebuffer = Renderer.getInstance().boundFramebuffer; + int framebufferWidth = boundFramebuffer.getWidth(); + int framebufferHeight = boundFramebuffer.getHeight(); + switch (pretransformFlags) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR -> { + offset2D.x(framebufferWidth - h - y); + offset2D.y(x); + } + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR -> { + offset2D.x(framebufferWidth - w - x); + offset2D.y(framebufferHeight - h - y); + } + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR -> { + offset2D.x(y); + offset2D.y(framebufferHeight - w - x); + } + default -> { + offset2D.x(x); + offset2D.y(y); + } + } + return offset2D; + } + + /** + * Transform the width and height from Minecraft coordinate space to the Vulkan coordinate space + * and write them to VkExtent2D + * @param extent2D the extent to which the values should be written + * @param w the viewport/scissor operation width + * @param h the viewport/scissor operation height + * @return the same VkExtent2D with transformations applied as necessary + */ + private static VkExtent2D transformToExtent(VkExtent2D extent2D, int w, int h) { + int pretransformFlags = Vulkan.getPretransformFlags(); + if(pretransformFlags == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || + pretransformFlags == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { + return extent2D.set(h, w); + } + return extent2D.set(w, h); + } + public static void setScissor(int x, int y, int width, int height) { try(MemoryStack stack = stackPush()) { - int framebufferHeight = Renderer.getInstance().boundFramebuffer.getHeight(); + VkExtent2D extent = VkExtent2D.malloc(stack); + Framebuffer boundFramebuffer = Renderer.getInstance().boundFramebuffer; + // Since our x and y are still in Minecraft's coordinate space, pre-transform the framebuffer's width and height to get expected results. + transformToExtent(extent, boundFramebuffer.getWidth(), boundFramebuffer.getHeight()); + int framebufferHeight = extent.height(); VkRect2D.Buffer scissor = VkRect2D.malloc(1, stack); - scissor.offset(VkOffset2D.malloc(stack).set(x, framebufferHeight - (y + height))); - scissor.extent(VkExtent2D.malloc(stack).set(width, height)); + // Use this corrected height to transform from OpenGL to Vulkan coordinate space. + scissor.offset(transformToOffset(VkOffset2D.malloc(stack), x, framebufferHeight - (y + height), width, height)); + // Reuse the extent to transform the scissor width/height + scissor.extent(transformToExtent(extent, width, height)); vkCmdSetScissor(INSTANCE.currentCmdBuffer, 0, scissor); } diff --git a/src/main/java/net/vulkanmod/vulkan/VRenderSystem.java b/src/main/java/net/vulkanmod/vulkan/VRenderSystem.java index d6e3a6c96..588122596 100644 --- a/src/main/java/net/vulkanmod/vulkan/VRenderSystem.java +++ b/src/main/java/net/vulkanmod/vulkan/VRenderSystem.java @@ -127,13 +127,22 @@ public static void applyModelViewMatrix(Matrix4f mat) { } public static void applyProjectionMatrix(Matrix4f mat) { - mat.get(projectionMatrix.buffer.asFloatBuffer()); + Matrix4f pretransformMatrix = Vulkan.getPretransformMatrix(); + FloatBuffer projMatrixBuffer = projectionMatrix.buffer.asFloatBuffer(); + // This allows us to skip allocating an object + // if the matrix is known to be an identity matrix. + // Tbh idk if the jvm will just optimize out the allocation but i can't be sure + // as java is sometimes pretty pedantic about object allocations. + if((pretransformMatrix.properties() & Matrix4f.PROPERTY_IDENTITY) != 0) { + mat.get(projMatrixBuffer); + } else { + mat.mulLocal(pretransformMatrix, new Matrix4f()).get(projMatrixBuffer); + } } public static void calculateMVP() { org.joml.Matrix4f MV = new org.joml.Matrix4f(modelViewMatrix.buffer.asFloatBuffer()); org.joml.Matrix4f P = new org.joml.Matrix4f(projectionMatrix.buffer.asFloatBuffer()); - P.mul(MV).get(MVP.buffer); } diff --git a/src/main/java/net/vulkanmod/vulkan/Vulkan.java b/src/main/java/net/vulkanmod/vulkan/Vulkan.java index 27e67cc56..8814924e4 100644 --- a/src/main/java/net/vulkanmod/vulkan/Vulkan.java +++ b/src/main/java/net/vulkanmod/vulkan/Vulkan.java @@ -5,12 +5,11 @@ import net.vulkanmod.vulkan.memory.MemoryManager; import net.vulkanmod.vulkan.memory.MemoryTypes; import net.vulkanmod.vulkan.memory.StagingBuffer; -import net.vulkanmod.vulkan.queue.GraphicsQueue; import net.vulkanmod.vulkan.queue.Queue; -import net.vulkanmod.vulkan.queue.TransferQueue; import net.vulkanmod.vulkan.shader.Pipeline; import net.vulkanmod.vulkan.texture.VulkanImage; import net.vulkanmod.vulkan.util.VUtil; +import org.joml.Matrix4f; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; import org.lwjgl.util.vma.VmaAllocatorCreateInfo; @@ -462,6 +461,13 @@ public static VkExtent2D getSwapchainExtent() return swapChain.getExtent(); } + public static Matrix4f getPretransformMatrix() { + return swapChain.getPretransformMatrix(); + } + public static int getPretransformFlags() { + return swapChain.getPretransformFlags(); + } + public static List getSwapChainImages() { return swapChain.getImages(); } public static long getCommandPool() diff --git a/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java b/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java index 32e80f80e..43bee7950 100644 --- a/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java +++ b/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java @@ -8,6 +8,7 @@ import net.vulkanmod.vulkan.Vulkan; import net.vulkanmod.vulkan.queue.Queue; import net.vulkanmod.vulkan.texture.VulkanImage; +import org.joml.Matrix4f; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.*; @@ -39,6 +40,12 @@ public static int getDefaultDepthFormat() { private long swapChain = VK_NULL_HANDLE; private List swapChainImages; private VkExtent2D extent2D; + // A matrix that describes the transformations that should be applied + // to the output of the game. + private Matrix4f pretransformMatrix = new Matrix4f(); + // The pretransform flags that were given to the swapchain, + // masked (see "setupPreRotation(VkExtent2D, VkSurfaceCapabilitiesKHR)") + private int pretransformFlags; public boolean isBGRAformat; private boolean vsync = false; @@ -83,6 +90,7 @@ public void createSwapChain() { VkSurfaceFormatKHR surfaceFormat = getFormat(surfaceProperties.formats); int presentMode = getPresentMode(surfaceProperties.presentModes); VkExtent2D extent = getExtent(surfaceProperties.capabilities); + setupPreRotation(extent, surfaceProperties.capabilities); if(extent.width() == 0 && extent.height() == 0) { if(swapChain != VK_NULL_HANDLE) { @@ -327,6 +335,14 @@ public VkExtent2D getExtent() { return extent2D; } + public Matrix4f getPretransformMatrix(){ + return pretransformMatrix; + } + + public int getPretransformFlags() { + return pretransformFlags; + } + public VulkanImage getColorAttachment() { return this.swapChainImages.get(Renderer.getCurrentFrame()); } @@ -393,6 +409,38 @@ private static VkExtent2D getExtent(VkSurfaceCapabilitiesKHR capabilities) { return actualExtent; } + private void setupPreRotation(VkExtent2D extent, VkSurfaceCapabilitiesKHR surfaceCapabilities) { + // Mask off anything else that does not interest us in the transform + pretransformFlags = surfaceCapabilities.currentTransform() & + (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | + VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | + VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR); + int rotateDegrees = 0; + boolean swapXY = false; + switch (pretransformFlags) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR -> { + rotateDegrees = 90; + swapXY = true; + } + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR -> { + rotateDegrees = 270; + swapXY = true; + } + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR -> rotateDegrees = 180; + } + pretransformMatrix = pretransformMatrix.identity(); + if(rotateDegrees != 0) { + pretransformMatrix.rotate((float) Math.toRadians(rotateDegrees), 0, 0, 1); + pretransformMatrix.invert(); + } + if(swapXY) { + int originalWidth = extent.width(); + int originalHeight = extent.height(); + extent.width(originalHeight); + extent.height(originalWidth); + } + } + public boolean isVsync() { return vsync; }