[OpenGL and Vulkan Interoperability on Linux] Part 9: Reusing a Vulkan z buffer from OpenGL

In this 9th post on OpenGL and Vulkan interoperability on Linux with EXT_external_objects and EXT_external_objects_fd we are going to see another extensions use case where a Vulkan depth buffer is used to render a pattern with OpenGL. Like every other example use case described in these posts, it was implemented for Piglit as part of my work for Igalia‘s graphics team to check the extensions implementation of various mesa drivers.

The vk-depth-display test is implemented in tests/spec/ext_external_objects/vk_depth_display.c in piglit.

[UPDATE]
The code is not upstream yet, but it can found in Piglit’s MR!329 that is (at the time of writing) under review. The code can be found in Piglit’s master branch, and has been

I’ve accidentally merged the 2 test-related commits without credits to the reviewer and so I thank him here…

Tapani Pälli reviewed all the other tests as well and wrote several patches for the EXT_external_objects(_fd) implementation on iris.

In vk-depth-display test:

  • A quad is rendered at a specific depth using Vulkan to fill the z buffer with a recognizable pattern.
  • This Vulkan z buffer is imported from OpenGL where we clear the framebuffer to green and render a blue fullscreen quad positioned on the z axis further away than the original quad.
  • We check if the z buffer has been imported correctly. If so, the blue quad should be partially obscured due to the depth test failing in the middle.
  • We repeat for images of different depth, or depth/stencil formats.


Step 1: An external z image is allocated and used in a Vulkan renderpass

We’ve seen how we allocate external images from Vulkan in Parts 2 and 3.

As in this test we check different depth|stencil formats, we have a subtest per format where we allocate external Vulkan images and use them in OpenGL rendering. Everything is implemented in run_subtest.

The images that are used in each subtests are allocated in vk_subtest_init. The external allocation has already been explained in Parts 2 and 3.

What is different in this test is the Vulkan renderer (remember that the renderer is the abstraction I use in the Vulkan framework in vk.[hc] to wrap all the pipeline/renderpass elements and operations that are required to render a scene). This test’s renderer can write values in the depth buffer attachment. To enable depth writes we need the value enable_depth to be set to true when the renderer is created with a call to vk_create_renderer:

With enable_depth set to true when creating the renderer and its renderpass and pipeline we also enable these depth/stencil settings in the pipeline (vk.[hc]):

VK_COMPARE_OP_LESS will write the depth to the z buffer only when it’s less than the value we used to clear it. Since clear the depth buffer to 1.0 when the renderpass begins (vk.[hc]) any value less than 1.0 will be overwritten.

Let’s see how the depth pattern is created:

The shaders used for rendering (see vk_create_renderer in the snippet above) are used to render something that is not important at all as the color image will not be used anywhere but is required for the Vulkan renderpass to begin. So the fragment shader is only painting a color. What is important though is the depth where we render the geometry. And so in the vertex shader we select a depth that is between 0 and 1 (here 0.33) and we draw a 2D quad in that depth.

When the Vulkan renderpass is over, the depth buffer will be an image with ones everywhere except the middle where we will have a quad of 0.33. Something like that:

.
.
.
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 0.33 0.33 ... 0.33 0.33 1.00 1.00 ...
... 1.00 1.00 0.33 0.33 ... 0.33 0.33 1.00 1.00 ...
.
.
.
... 1.00 1.00 0.33 0.33 ... 0.33 0.33 1.00 1.00 ...
... 1.00 1.00 0.33 0.33 ... 0.33 0.33 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
... 1.00 1.00 1.00 1.00 ... 1.00 1.00 1.00 1.00 ...
.
.
.


Step 2: The external z buffer is used from OpenGL

In order to use the z-buffer from OpenGL we must first import the Vulkan depth attachment. This is similar to what we did in Parts 2, 3 and 4) to import the color attachment:

Then, we need to render something that can help us test if the import was successful. And so,

  • We render a blue full screen quad in depth 1.0 with the depth test enabled and using the external depth buffer as depth attachment to the target framebuffer.
  • We display the result with depth test enabled. We expect to see a blue quad that has a small quad with the framebuffer color in the middle because in the middle we had depth = 0.33 < 1 and so the blue color of the quad must be "obscured" by the framebuffer color.

To render from OpenGL using the Vulkan depth buffer we need to attach it to an OpenGL FBO that will serve as the OpenGL render target. We've created such an FBO in gl_init (called from piglit_init) and we can call gl_subtest_init per subtest to attach each subtest's depth image (we run a subtest for each depth/stencil format supported in the driver) and then render a quad to its color attachment with the depth test enabled at a depth that is further away from the original 0.33 (here I used 0.66):

After this step, we are ready to display the color attachment to validate that the import was successful.


Step 3: Results validation

Back to run_subtest we display the result and probe the color value in a few points to see if we detect green (framebuffer color) in the middle of the image and blue elsewhere:

And that's it. In this test we enabled the depth test in a Vulkan renderpass and we imported the renderpass's depth attachment in OpenGL. We used the imported depth buffer as depth attachment in an OpenGL FBO and we've rendered a pattern with the depth test enabled. At the end we've validated that the pixels we expected to be obscured were obscured which means the z-buffer was imported successfully.


Links:


What's next:

Next post will be about reusing an external Vulkan stencil buffer from OpenGL. Stay tuned!


and see you next time!

Leave a Reply

Your email address will not be published. Required fields are marked *