[OpenGL and Vulkan Interoperability on Linux] Part 5: A Vulkan pixel buffer is reused by OpenGL

This is the 5th post of the OpenGL and Vulkan interoperability series where I describe some use cases for the EXT_external_objects and EXT_external_objects_fd extensions. These use cases have been implemented inside Piglit as part of my work for Igalia‘s graphics team using a Vulkan framework I’ve written for this purpose.

And in this 5th post, we are going to see a case where a pixel buffer is allocated and filled by Vulkan and its data are used as source data for an OpenGL texture image.

The code of vk-buf-exchange test can be found in tests/spec/ext_external_objects/ Piglit directory, inside vk_buf_exchange.c. The test:

  • Creates an external Vulkan buffer with the same stripped pattern we’ve used in previous tests.
  • Imports the pixel buffer in OpenGL.
  • Uses this buffer as pixel storage for an OpenGL texture and displays its content on the screen.
  • Validates the displayed result.

Step 1: External buffer creation from Vulkan

We’ve already seen in the previous OpenGL and Vulkan Interoperability posts that all tests have a piglit_init function where we initialize the Vulkan objects calling vk_init and the GL objects calling gl_init and we usually call some functions to import the shared objects in OpenGL.

In this test’s initialization we need to create an external Vulkan buffer, import it from OpenGL and then use it. As we already have some code that renders to Vulkan images in the framework, we can fill it by copying to it pixels from a Vulkan image. And so, we first render the stripped pattern we’ve used in most previous examples using Vulkan on an image, and then we copy the image pixels to the external buffer we’ve just allocated.

External buffer allocation in vk_init:

This framework (vk.[hc]) function is creating a buffer but links a VkExternalMemoryBufferCreateInfo struct in VkBufferCreateInfo‘s pNext pointer (see also vk_create_buffer in vk.[hc]):

By setting the VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR bit the memory can be used by OpenGL.

Step 2: Importing the buffer in OpenGL

First (we are still in piglit_init) we import the memory object as we’ve done in all previous examples:

and then we create a buffer from the memory object calling:

This function is very similar to gl_gen_tex_from_mem_obj we’ve seen in Parts 2, 3, and 4:

We called it with GL_PIXEL_UNPACK_BUFFER as target because as we are going to see later this particular pixel buffer will be the data source for an OpenGL texture.

The EXT_external_objects glBufferStorageMemEXT function is very similar to glBufferStorage but creates the storage from the imported memory object mem_obj.

Step 3: Filling the Vulkan buffer from Vulkan

An easy way to fill the pixel buffer from Vulkan is to render to a color image (like in Parts 3 and 4) and then copy the pixels from the image to the buffer. We do so in piglit_init:

(Both vk_draw and vk_copy_image_to_buffer can be found in vk.[hc]).
The pattern that we’ve rendered is again the colored bars image we’ve seen in Parts 3 and 4:

I used that image in all examples that render pixels because it’s convenient: I can easily get an insight of the displayed pattern by probing the middle pixel of each bar.

After that, we can use the buffer from OpenGL.
Note here that we must make sure (with barriers) that there are no pending Vulkan operations when OpenGL takes the control. We use a barrier in vk_draw to make sure the rendering has been completed before copying the pixels and one in vk_copy_image_to_buffer to make sure copy has also been completed before we use the buffer from OpenGL.

Step 4: Using the buffer from OpenGL

In OpenGL we are going to use the buffer pixels in a texture to display them. And as a first step we need to create that texture. In gl_init called by piglit_init we create that texture:

Then, we fill it calling glTexImage2D with the buffer data. To do so we need to bind the external buffer as GL_PIXEL_UNPACK_BUFFER as it’s the texture data source!

Next step is to display the texture and validate the pixel values.
In piglit_display we map the texture on a quad and use the piglit built-in functions to probe the middle color of each band to make sure the stripped pattern had been rendered by Vulkan:

And that was it: we’ve just allocated an external buffer, filled it with some Vulkan image’s pixel, we imported it in OpenGL and bound it as PIXEL_UNPACK_BUFFER to use it as source data for an OpenGL texture. At the end we displayed that OpenGL texture. The test was expected to display the stripped pattern and pass.


What’s next:

Next post will be again about pixel buffers, a variation of this one. Follow-up posts will be about external vertex buffers, stay tuned!

and see you next time!

Leave a Reply

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