Advertisement

How to reuse and repeatedly Update a single VertexBuffer for multiple draws in Vulkan?

Started by January 24, 2019 11:33 PM
4 comments, last by hongkun 6 years ago

Hi,

I have been coding with Vulkan API for couple of months. I have got basic ideas about the low-level design pattern of Vulkan and its retained mode nature for the performance purpose.

For example, I'm aware that we should avoid as much as possible unnecessary per-frame buffer (uniform, vertex, index, texture, etc.) updating and DescriptorSet binding for better performance.

However, in my project, there is a particular sub-module like a tiny 2D graphics library that allows 3D applications draw arbitrary overlaying 2D shapes in an old immediate coding manner, like the following pseudo code:


engine2D->drawPolyline( points1.data(), points1.size(), Color::Red, false);//draw an outlined polyline

engine2D->drawPolyline( points2.data(), points2.size(), Color::Blue, true);//Fill a polyline (polyong)

engine2D->drawPolyline( points3.data(), points3.size(), Color::Blue, true);//Fill another polyline (polyong)

... // Draw other 2D shapes needed (rect, ellipse, polyline, polyong ...)

Here is my function interface to draw polyline/polygon in my 2D engine:


void GraphicsEngine2D::drawPolyline(Point * points, int pointCount, int color, bool fillInterior = false, int transparency = 0, int lineThickness = 1)

In my Vulkan implementation, I plan to:

  •  Create a vkBuffer used as 2D vertex buffer (vec2 as elements) with capability up to 1024 points
  •  Keep the vkBuffer mapped so that an address in host memory is always available
  •  Rule: drawPolyline() can only be called inside a Vulkan renderPass (somewhere between beginRenderPass() and endRenderPass())
  •  For each call to drawPolyline(), what I can do is like:

beginRenderPass()

    other 2D/3D drawing goes here ...

    drawPolyline(points1, ...):
        Step 1. Update the vertex buffer with the points data passed in by caller 
        Step 2. Bind the vertex buffer
        Step 3. Bind other related stuff (descriptor set, push constants, which is irrelevant to my topic ...)
        Step 4. Call cmdDraw() to draw the polyline or polygon

    drawPolyline(points2, ...):
        Step 1. Update the vertex buffer with the points data passed in by caller 
        Step 2. Bind the vertex buffer
        Step 3. Bind other related stuff (descriptor set, push constants, which is irrelevant to my topic ...)
        Step 4. Call cmdDraw() to draw the polyline or polygon

    drawPolyline(points3, ...):
		...

    other 2D/3D drawing goes here ...

endRenderPass()

Since I use a shared vertex buffer dedicated to function drawPolyline(), due to the retained manner of Vulkan command execution, updating vertex buffer repeatedly during the command buffer recording will not work until I put something like pipeline barrier between each call to drawPolyline().

I am aware that most of experienced Vulkan developers may point it out my design will compromise the Vulkan performance due to updating vertex buffer in such interleaving manner, they may also suggest me to create large numbers of vertex buffer in advance dedicate to each call to drawPolyine().

But my question is that: without performance consideration, how can I implement an immediate mode function drawPolyline() to allow application programmers call it as many time as they want to get all polylines. Remember, the numbers of polylines, its color, whether outlined or filled are all unpredictable in advance, the function is just like a function in a generic 2D graphics library.

Thanks for any suggestions.

You can have an immediate-mode API without actually submitting immediate-mode-like API calls to Vulkan. I would use your existing API, but build up a single vertex buffer and related state over the course of the frame (or multiple based on differences in pipeline, say 1 for wireframe and another for filled tris). Then at the end of the frame you submit a single draw per buffer.

Advertisement
10 hours ago, Valakor said:

You can have an immediate-mode API without actually submitting immediate-mode-like API calls to Vulkan. I would use your existing API, but build up a single vertex buffer and related state over the course of the frame (or multiple based on differences in pipeline, say 1 for wireframe and another for filled tris). Then at the end of the frame you submit a single draw per buffer.

Thanks Valakor for your suggestion.

I'd like to confirm I understand your suggestion. Take the example of drawing polyline again, I can prepare a single VertexBuffer with "pretty large" capacity, every time the drawPolyine() is called, I store the passed-in points inside this vertex buffer and record the starting index in the buffer. at the final draw time, I bind this single vertex buffer and call vkCmdDraw() multiple times with recorded offset as the parameter "firstVertex", so all polylines are drawn?

I can imagine that the only way to implement an immediate API above an retained API like vulkan is via simulation, still record all immediate drawing called first, then submit them at the draw time...

You could call Draw multiple times, but since you're drawing primitive data (colored lines basically), you can probably just submit a single vkCmdDraw per large vertex buffer. (You've essentially recorded all the 'commands' you need inline in the vertex buffer, as long as you don't need any state changes between them).

If you need more state changes, then yes you'll probably need to record your own mini command-buffer format while building up a vertex buffer over the frame, then replay those commands with appropriate state changes during your render pass.

54 minutes ago, Valakor said:

You could call Draw multiple times, but since you're drawing primitive data (colored lines basically), you can probably just submit a single vkCmdDraw per large vertex buffer. (You've essentially recorded all the 'commands' you need inline in the vertex buffer, as long as you don't need any state changes between them).

If you need more state changes, then yes you'll probably need to record your own mini command-buffer format while building up a vertex buffer over the frame, then replay those commands with appropriate state changes during your render pass.

Thanks, I got your idea.

This topic is closed to new replies.

Advertisement