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.