Repository navigation
Description
Keeping in mind that we should only surface concepts in Flutter GPU that are expected to provide demonstrable value, I started contemplating whether the CommandBuffer concept is valuable enough to justify having (again) while researching potential synchronization issues and re-analyzing our current usage of Impeller's HAL.
At some point early on, I had actually removed CommandBuffer from Flutter GPU, but eventually decided to add it back while prototyping. So I wanted to record my thoughts and discuss more openly.
TL;DR, I don't suspect there's any compelling performance reason to keep it, but removing it would limit our options for adding convenient blit functions and such.
The CommandBuffer concept is similar to Metal's MTLCommandBuffer, in that it's the top-level command encoding API. Submitted CommandBuffers cannot be reused. This is a completely different concept to e.g. Vulkan's VkCommandBuffer, which is a reusable list of commands (and may reference secondary command buffers).
Risks of removal
Future usability
As we continue adding more features to the API, having a "command encoder" like CommandBuffer may become a clear winner from a usability standpoint.
For example, we don't need to surface everything as a Pass like with Metal. RenderPass obviously makes a lot of sense for tiler GPUs, but BlitPass? Not really.
Rather than surfacing a BlitPass, we should probably just surface simple blit functions on CommandBuffer for generating mipmaps, copying textures to other textures, copying buffers to textures, etc.
Performance
One (minuscule) benefit of CommandBuffer is slightly less overhead when targeting Metal, given that we'd need to create separate MTLCommandBuffers for every pass otherwise.
If we didn't have a command encoder concept like CommandBuffer, we'd pretty much lose the option to surface pass-less blits in the API.
Adding such blit functions to GpuContext directly would be a Very Bad thing, as each call would end up needing to trigger a queue submission.
Current design
As described in #150816, Flutter GPU's current rasterization workflow looks like this:
- Get the GPU context.
- Create a
CommandBufferwithGpuContext.createCommandBuffer. - Create a
RenderPasswithCommandBuffer.createRenderPass. - Set state/pipelines and bind resources with various
RenderPassmethods. - Append draw commands with
RenderPass.draw. - Submit the
CommandBufferwithCommandBuffer.submit(async). AllRenderPasses are encoded in the order with which they were created.
One potential point of friction with this design comes from the fact that we automatically encode each RenderPass on CommandBuffer submit. Which means that renderers are forced to define/create all "subpasses" in advance if they wish to encode everything to a single CommandBuffer.
TODO(bdero): In Impeller's 2D renderer, we've tried to exploit the ability to encode RenderPasses out of order the past by e.g. recording all EntityPass to one CommandBuffer. Cross reference the safety issues that have come up in the past while attempting to do this in Impeller's 2D renderer.
However, Impeller's 2D renderer today does not even rely on the ability to choose the RenderPass encoding order. Impeller's 2D renderer happens to encode every RenderPass to its CommandBuffer in creation order.
- For
EntityPasssubpasses,InlinePassContextcreates and separately submits a newCommandBufferfor each newRenderPass. The optional async completion callback is not used. - For filters that perform extra
RenderPasses, the Filter creates a newCommandBufferand adds a sequence ofRenderPasses to it using theContentContext::MakeSubpass. This utility encodes theRenderPassimmediately, and Filters never use this utility to create subpasses within the sameCommandBuffer.
How would removing CommandBuffer change the API?
Without a CommandBuffer, the rasterization workflow would change to look like this:
- Get the GPU context.
- Create a
RenderPasswithGpuContext.createRenderPass. - Set state/pipelines and bind resources with various
RenderPassmethods. - Append draw commands with
RenderPass.draw. - For submission, there are a couple of options:
- Single submission: Submit the
RenderPasswithRenderPass.submit(async). AllRenderPasses are encoded in the order with which they were created. - Batch submission: Submit a list of
Passes withGpuContext.submit(List<Pass>)
- Single submission: Submit the
For 5, I'm leaning towards just having GpuContext.submit(List<Pass>) and nothing else.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fields✅ Done
Activity
bdero commentedon Jul 8, 2024
I've decided to keep
CommandBufferfor the usability reasons above.github-actions commentedon Jul 23, 2024
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.