Restart

Primitive Restart

The last remaining feature for GL 3.1 was primitive restart, which allows an indexed draw command to end the current primitive when a specified index is processed, beginning a new one of the same type with the next index. Other than the minor changes of enabling the driver capability, there were two main issues with translating this functionality to Vulkan:

  • OpenGL primitive restart allows for an arbitrary restart index to be specified by the user, while Vulkan always uses (uint[size]_t)-1
  • OpenGL primitive restart works on all primitive types, but Vulkan only allows it for strips and fans without adjacency

I decided to start with the first issue.

Indexing

As with many problems in driver world, this is one that people have had previously, which meant that the work I needed to do was limited. In fact, it was as simple as checking in zink_draw_vbo() whether the draw command was using the Vulkan index and, if it wasn’t, remapping it using an existing function.

uint32_t restart_index;
if (dinfo->index_size == 1)
   restart_index = (uint8_t)-1;
else if (dinfo->index_size == 2)
   restart_index = (uint16_t)-1;
else if (dinfo->index_size == 4)
   restart_index = (uint32_t)-1;
else
   unreachable("unknown index size passed");
if ((dinfo->primitive_restart && (dinfo->restart_index != restart_index)) || !screen->have_EXT_index_type_uint8) {
  util_translate_prim_restart_ib(pctx, dinfo, &index_buffer);

Note here that there’s handling for the case of missing VK_EXT_index_type_uint8, which is required in order to use index buffers of size uint8_t.

If this codepath is taken, a new index buffer with the restart values rewritten to Vulkan ones is created using the specified range, which means the draw command now starts from index zero.

Primitive types

Again, this is not a new problem that I’d discovered, which meant that, after being pointed in the right direction by Dave Airlie, this was just a few lines of code:

void
zink_draw_vbo(struct pipe_context *pctx,
              const struct pipe_draw_info *dinfo)
{

   if (dinfo->primitive_restart && !restart_supported(dinfo->mode)) {
       util_draw_vbo_without_prim_restart(pctx, dinfo);
       return;
   }

restart_supported() here just checks the primitive draw mode for a Vulkan-supported strip/fan type.

That’s actually all that was required, and now primitive restart works*.

  • Except when it breaks gl_PrimitiveID in geometry shaders, which I’ll get to in a future post.
Written on July 9, 2020