Skip to content

Performance improvements - #117

Open
attackgoat wants to merge 30 commits into
mainfrom
performance
Open

attackgoat wants to merge 30 commits into
mainfrom
performance

Conversation

@attackgoat

@attackgoat attackgoat commented Aug 26, 2026 •

Copy link
Copy Markdown
Owner

Various internal pattern changes and optimizations found while testing.

Changes

Backwards compatible:

  • Support coarse image ownership tracking (whole/dual/dense, like access has single/dual/dense)
  • Optimize both ownership and access patterns now that they have similar APIs
  • Optimize solver with "same resource list" detection/re-use
  • Skip barriers if possible (tried twice before, this needs many tests)
  • Add resource-set API to make "bind my materials" faster
  • Continue compatible subpasses when possible

Breaking:

  • Add threaded command buffer finalizer (disposer): New DeviceInfo field to enable this
  • Add opacity micro maps: Tidied-up acceleration structure build APIs and new field for OMM

@attackgoat

Copy link
Copy Markdown
Owner Author

The "same resource list" optimization makes a big difference if the workload is "lots of materials and a smaller collection of program-related resources". This is a micro-bench of a very small section of the submission process but a good first target:

New Bench Before After
10 commands 55.9 us 5.6 us
11 commands 67.6 us 5.7 us
25 commands 158.2 us 9.1 us

@attackgoat

Copy link
Copy Markdown
Owner Author

The new resource set API is additive and allows repeated arrays of images and acceleration structures to have a single bind/access operation as a group instead of one by one for each resource+command. This optimization can be combined with prepared command streams and descriptor sets.

This per-resource tax became quite a burden in real-world use:

  • Images used as materials often need to be bound and used in many additional commands
  • Acceleration structures built into a TLAS require read access in many later commands

There are probably also reasonable "I have a lot of buffers here" issues that come up, but I don't have any of those workloads right now so nothing has been designed for them.

There are probably also more reasonable access types to add (transfers, writes) for images and acceleration structures which could be helpful - those should be explored when the need arises because they might upset some of the sampled-only optimizations. Additions may wait until bugs are found/fixed.

I applied these changes in a test program and found significant performance gains by reducing the CPU overhead, example:

// Construct; each Arc<Image> is one descriptor slot.
let image_set = ImageSet::new(images.iter())?;

// Bind to the graph and declare sampled-read access.
let image_set_node = graph.bind_resource(&image_set);
graph
    .begin_cmd()
    .resource_access(image_set_node, ImageAccessType::SampledRead)
    .record_cmd(|cmd| {
        // Bind/use your descriptor set and issue commands here.
    })
    .end_cmd();

For subresources, pass (Arc<Image>, vk::ImageSubresourceRange) entries. ImageSet tracks synchronization.

Note:
Each new set type has a new type-specific AccessType which contains all the supported accesses for each resource set.

Note:
Do not bind an individual resource set member in the same graph - it will panic. We have the option of folding these accesses together, but you can avoid this by binding individual members on the conflicting frames and the whole set on other frames.

Note:
Descriptor-set binding remains separate and for the most performance implement both:

  • AccelerationStructureSet/ImageSet: access and graph optimization
  • DescriptorSet: Vulkan driver optimization

Comment thread src/lib.rs Fixed
@attackgoat
attackgoat marked this pull request as ready for review September 8, 2026 10:35
@attackgoat

Copy link
Copy Markdown
Owner Author

This will stay in review for another week or so just in case more improvements become obvious

I tried to keep this to patch version changes, but the acceleration structure API was both an area of particularly poor design and also did not support opacity micromaps. All in, the public changes are extremely small.

Passing this branch against the exact same demo project with only this branch supported and no other changes gives an impressive results:

  • CPU Speed-up: 5.7x
  • Present time: 10.0 ms -> 1.75 ms
  • CPU usage drops; GPU utilization increases, FPS 1.3x
performance

@attackgoat

Copy link
Copy Markdown
Owner Author

The recent changes have allowed me to increase the quality of this full-path-tracer sample and still get higher performance, key metric for this library is the cpu time for this frame (other than per-game logic) is about ~1 ms to finalize and submit the frame graph. This includes 285 M triangles and 16 animated 1-blend characters, sky/water/clouds, fully dynamic everything no temporal.

I think I will begin to wrap up the branch and get v0.15 started, though this may take a week or more.

Screenshot From 2026-09-25 11-25-50

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Changing the access descriptor for a Uniform multiple times within the same pass.

2 participants