Flipping the Vulkan viewport

Introduction

This short tutorial deals with Vulkan’s viewport setup, which differs from the one in OpenGL and other APIs. I’ll try to explain what it takes to get your (OpenGL) scene rendered properly, and how e.g. VK_KHR_MAINTENANCE1 can help you deal with differences across the APIs, something that’s esp. helpful if you try to add Vulkan as a backend to you renderer without wanting to alter your actual scene data.

The problem

Passing your vertex data jus like in OpenGL, using the same state (as the pipeline setup) and shaders as in OpenGL, your scene will likely not display as you’d expect.

Take this demo that is supposed to render the Sibenik Cathedral in Vulkan with the same vertex data and pipeline setup as in a similar OpenGL example:

This was rendered with:

  • Y values of the vertex data pointing up
  • Back face culling enabled
  • Winding order set to counter clockwise

Comparing it with how the scene should be rendered:

It’s easy to see what’s actually wrong here:

  • The scene is rendered upside down
  • Culling is removing front faces instead of back faces

Different coordinate systems

The cause for this is pretty simple: Vulkan is using a different coordinate system.

The viewport’s origin in OpenGL is in the lower left of the screen, with Y pointing up. In Vulkan the origin is in the top left of the screen, with Y pointing downwards. The latter part explains why our scene is rendered upside down.

Quick and dirty

So a quick and easy fix would be just inverting all your y coordinates, either on the host or in the vertex shader like this:

void main() 
{
	...
	gl_Position.y = -gl_Position.y;	
}

Even though that would work, you’d have to add this to every vertex shader, which would be especially annoying when working on a renderer with multiple backends sharing the same shaders.

Flipping the viewport

Luckily Khronos realized that the different coordinate system could possibly impede Vulkan adoption and decided to fix this. Designing an API is not an easy task, especially when it has to cover a wide range of operating systems, platforms and devices.

So after collecting developer feedback, the VK_KHR_Maintenance1 extensions was added for Vulkan 1.0, including, among other API fixes, support for passing negative viewport heights:

Allow negative height to be specified in the VkViewport::height field to perform y-inversion of the clip-space to framebuffer-space transform. This allows apps to avoid having to use gl_Position.y = -gl_Position.y in shaders also targeting other APIs.

Note: The extension has become core with Vulkan 1.1. So if you only target this Vulkan version (and up), you can flip the viewport without having to check for the extension.

So when setting the viewport state we can now simply specify a negative viewport height to flip it along the y axis:

1
2
3
4
5
6
7
8
    VkViewport viewport{};
	
    viewport.width = renderer.extent.width;
	viewport.height = -renderer.extent.height;
    viewport.x = 0;   
	viewport.y = renderer.extent.height;

    vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);

Note that you also need to change the viewport’s origin (line 6), or you won’t see anything of your scene as the whole viewport is outside of the window.

With this setup you can now use the same shaders, vertex data and pipeline setup as e.g. in your OpenGL app.

Sample

I also have added a new example to demonstrate how flipping the viewport in Vulkan works, how Vulkan compares to e.g. OpenGL, and how the different pipeline states affect the viewport.