Sign in to follow this  
Ryan_001

Vulkan Vulkan UI rendering

Recommended Posts

I'm working on a UI that uses Vulkan for rendering.  I've come across a conundrum of sorts.  The general structure as it stands is:

- Window class handles interfacing with OS.  Each Window contains a number of Frame's (5-10'ish range, not a large number).

- Frame classes serve as containers for controls.  Frame's are mostly there to facilitate different rendering techniques (different shaders, descriptors sets, etc...), as well as differences in update frequency.

- Controls, the buttons, menus, etc... you actually see/work with, are contained in Frame's.

I had ideas for 3 different ways to approach this:

1) Have each Frame record a secondary command buffer.  To render the Window a primary commmand buffer would be created executing the secondary buffers within a render pass.  That way I'd only have to update the secondary command buffers when their corresponding Frame changes, the Frame secondary buffers would be VkFrameBuffer independent, and leads itself to easy multi-threading.  The downside is that the entire window would need to be rendered every frame.  For a video game this isn't a problem, but for apps/utilities this is unnecessary most of the time.

2) Store a 'dirty' rect/area.  When rendering simply create one large primary framebuffer that contains all necessary rendering.  This has the advantage of only rendering what actually changes, but means there would be almost no re-use of the command buffers.  Every change would almost always necessitate a completely new command buffer.

3) Have each Frame create its own primary command buffer.  This would make 'dirty' rect/area updates relatively easy, as well as allow command buffer reuse for some situations.  The downside is there would still be much less command buffer reuse than option 1, and rendering would not occur within a single render pass.  The docs aren't entirely clear, and since each GPU is different, its hard to gauge how much slower splitting the rendering across 5-10 separate command buffers/render passes would be than having one large one.

How would you guys go about this?  What method is better and/or what are you guys using?

Share this post


Link to post
Share on other sites

I question the need to only render a small portion of the UI at a time. Maybe it was useful back in the day before we had hardware acceleration and blitting pixels in the CPU was slow, but if you're using hardware accelerated rendering...I don't really think it's a problem redrawing the whole UI when something changes.

I did a small immediate mode UI for a basic map editor in opengl. I filled an array of all the vertices, copied them to the VBO, and rendered everything....every frame (and this was split up into multiple draw calls if the scissor rect changed). There was very little performance hit (everything, including the map itself, was being push out in <1ms iirc). In a UI that isn't limted to real time framerate of a video game..it's even less of a problem since acceptable delay is much more lenient. So if it takes 5 or even 10 ms to render your whole UI, it wouldn't cause noticeable lag in your application's usage.  

Edited by CirdanValen

Share this post


Link to post
Share on other sites

On desktop GPU's, you obviously have a lot of power at your disposal, also if your UI has transparent portions you are of course going to have to re-draw the entire thing.  For mobile though, wouldn't it make sense to try to cut back updates?

I guess you have a CPU/GPU tradeoff.  If I re-create the command buffer each frame, I put more stress on the CPU but can get optimal GPU usage.  If I cache the command buffers I can reduce stress on the CPU, but potentially increase GPU stress.  At this point I'm leaning towards recreating the command buffer each frame (#2 above).

Share this post


Link to post
Share on other sites
I don't have a truly "scientific, fact-based" answer for you, but my approach is "just draw it all", and I believe that is a good one. Why? For one reason it's simple. Simple is good. I'm stupid, and the simpler it is the fewer mistakes I make, the fewer time I spend tearing my hair. And then, the single one dominating thing on a GPU which never seems to get significantly better over the years is ROP and since you are (presumably) not writing to the screen buffer directly but use double-buffering (I wouldn't even know how to do something different with Vulkan anyway, but maybe that is possible) this means that you have to write every pixel one way or the other. Which of course means you ROP every pixel, whether you burn extra GPU memory (which is [i]not necessarily abundant[/i] on a mobile device) to save a few vertex shader cycles or not. That, and let's assume you could indeed only write the pixels that change. This would mean you can no longer pass the "undefined" and "don't care" flags to Vulkan (because you [i]do care[/i] about the previous contents, and it must be well-defined!), which presumably means the driver has to keep data from the previous frame around longer and change write-and-forget operations to read-modify-write. Or something, whatever. In any case, my bet is it will not come for free.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Forum Statistics

    • Total Topics
      628282
    • Total Posts
      2981812
  • Similar Content

    • By mellinoe
      Hi all,
      First time poster here, although I've been reading posts here for quite a while. This place has been invaluable for learning graphics programming -- thanks for a great resource!
      Right now, I'm working on a graphics abstraction layer for .NET which supports D3D11, Vulkan, and OpenGL at the moment. I have implemented most of my planned features already, and things are working well. Some remaining features that I am planning are Compute Shaders, and some flavor of read-write shader resources. At the moment, my shaders can just get simple read-only access to a uniform (or constant) buffer, a texture, or a sampler. Unfortunately, I'm having a tough time grasping the distinctions between all of the different kinds of read-write resources that are available. In D3D alone, there seem to be 5 or 6 different kinds of resources with similar but different characteristics. On top of that, I get the impression that some of them are more or less "obsoleted" by the newer kinds, and don't have much of a place in modern code. There seem to be a few pivots:
      The data source/destination (buffer or texture) Read-write or read-only Structured or unstructured (?) Ordered vs unordered (?) These are just my observations based on a lot of MSDN and OpenGL doc reading. For my library, I'm not interested in exposing every possibility to the user -- just trying to find a good "middle-ground" that can be represented cleanly across API's which is good enough for common scenarios.
      Can anyone give a sort of "overview" of the different options, and perhaps compare/contrast the concepts between Direct3D, OpenGL, and Vulkan? I'd also be very interested in hearing how other folks have abstracted these concepts in their libraries.
    • By GuyWithBeard
      Hi,
      In Vulkan you have render passes where you specify which attachments to render to and which to read from, and subpasses within the render pass which can depend on each other. If one subpass needs to finish before another can begin you specify that with a subpass dependency.
      In my engine I don't currently use subpasses as the concept of the "render pass" translates roughly to setting a render target and clearing it followed by a number of draw calls in DirectX, while there isn't really any good way to model subpasses in DX. Because of this, in Vulkan, my frame mostly consists of a number of render passes each with one subpass.
      My question is, do I have to specify dependencies between the render passes or is that needed only if you have multiple subpasses?
      In the Vulkan Programming Guide, chapter 13 it says: "In the example renderpass we set up in Chapter 7, we used a single subpass with no dependencies and a single set of outputs.”, which suggests that you only need dependencies between subpasses, not between render passes. However, the (excellent) tutorials at vulkan-tutorial.com have you creating a subpass dependency to "external subpasses" in the chapter on "Rendering and presentation", under "Subpass dependencies": https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation even if they are using only one render pass with a single subpass.
      So, in short; If I have render pass A, with a single subpass, rendering to an attachment and render pass B, also with a single subpass, rendering to that same attachment, do I have to specify subpass dependencies between the two subpasses of the render passes, in order to make render pass A finish before B can begin, or are they handled implicitly by the fact that they belong to different render passes?
      Thanks!
    • By mark_braga
      I am looking at the SaschaWillems subpass example for getting some insight into subpass depdendencies but its hard to understand whats going on without any comments. Also there is not a lot of documentation on subpass dependencies overall.
      Looking at the code, I can see that user specifies the src subpass, dst subpass and src state, dst state. But there is no mention of which resource the dependency is on. Is a subpass dependency like a pipeline barrier. If yes, how does it issue the barrier? Is the pipeline barrier issued on all attachments in the subpass with the input src and dst access flags? Any explanation will really clear a lot of doubts on subpass dependencies.
      Thank you
    • By mark_braga
      I need to index into a texture array using indices which are not dynamically uniform. This works fine on NVIDIA chips but you can see the artifacts on AMD due to the wavefront problem. This means, a lot of pixel invocations get the wrong index value. I know you fix this by using NonUniformResourceIndex in hlsl. Is there an equivalent for Vulkan glsl?
      This is the shader code for reference. As you can see, index is an arbitrary value for each pixel and is not dynamically uniform. I fix this for hlsl by using NonUniformResourceIndex(index)
      layout(set = 0, binding = 0) uniform sampler textureSampler; layout(set = 0, binding = 1) uniform texture2D albedoMaps[256]; layout(location = 0) out vec4 oColor; void main() { uint index = calculate_arbitrary_texture_index(); vec2 texCoord = calculate_texcoord(); vec4 albedo = texture(sampler2D(albedoMaps[index], textureSampler), texCoord); oColor = albedo; } Thank you
    • By Mercesa
      As the title says, I am explicitly creating a too small descriptor pool, which should NOT support the resources I am going to allocate from it.
       
      std::array<vk::DescriptorPoolSize, 3> type_count; // Initialize our pool with these values type_count[0].type = vk::DescriptorType::eCombinedImageSampler; type_count[0].descriptorCount = 0; type_count[1].type = vk::DescriptorType::eSampler; type_count[1].descriptorCount = 0; type_count[2].type = vk::DescriptorType::eUniformBuffer; type_count[2].descriptorCount = 0; vk::DescriptorPoolCreateInfo createInfo = vk::DescriptorPoolCreateInfo() .setPNext(nullptr) .setMaxSets(iMaxSets) .setPoolSizeCount(type_count.size()) .setPPoolSizes(type_count.data()); pool = aDevice.createDescriptorPool(createInfo);  
      I have an allocation function which looks like this, I am allocating a uniform, image-combined sampler and a regular sampler. Though if my pool is empty this should not work?
      vk::DescriptorSetAllocateInfo alloc_info[1] = {}; alloc_info[0].pNext = NULL; alloc_info[0].setDescriptorPool(pool); alloc_info[0].setDescriptorSetCount(iNumToAllocate); alloc_info[0].setPSetLayouts(&iDescriptorLayouts); std::vector<vk::DescriptorSet> tDescriptors; tDescriptors.resize(iNumToAllocate); iDevice.allocateDescriptorSets(alloc_info, tDescriptors.data());  
  • Popular Now