Skip to content

(WIP) Refactor asset loading - #22

Open
patowen wants to merge 72 commits into
masterfrom
refactor-vulkan-wip
Open

(WIP) Refactor asset loading#22
patowen wants to merge 72 commits into
masterfrom
refactor-vulkan-wip

Conversation

@patowen

@patowen patowen commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Very rough draft of new form of asset loading.

When testing this, I edited lahar::ParallelQueue to specify .flags(vk::SemaphoreWaitFlags::ANY) in its park method, as that is surprisingly not the default.

Recommendations for reviewing

  • The most important changes are in png_array.rs (used as an example for the new system) and asset_loader.rs (the new asset loader logic)
  • Hiding whitespace will help highlight the changes in png_array.rs. I elected not to do some of the refactors I originally planned yet, such as copying image data into Rust managed memory before copying into a Vulkan buffer, because I would be able to make the diff smaller that way, but only if whitespace is hidden, as the indentation level has changed.
  • The "offset_allocator" module can be ignored. That is a dependency I vendored into the branch for later, but it is not in use so far.

@patowen patowen changed the title Refactor vulkan wip (WIP) Refactor asset loading Aug 2, 2026
@Ralith

Ralith commented Aug 3, 2026

Copy link
Copy Markdown

I added a timeout of 1 second to lahar::ParallelQueue so that park won't block indefinitely

This shouldn't be necessary; waking it up on demand is what the passed-in semaphore is for.


pub fn transforms_mut(&mut self) -> &mut [na::Matrix4<f32>] {
&mut self.transforms
unsafe { self.transforms.as_mut() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: best practice (which we're absolutely not following consistently in existing code) is to always include a comment on unsafe blocks explaining why the operations within are sound, with the possible exclusion of blindingly obvious cases. In this example, it would explain why there's no aliasing hazard.

@patowen patowen Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the heads up. I'll look into this during my final polishing pass.

EDIT: Or now, now that I realize that what I'm looking for is concurrent writes by the GPU

EDIT2: I'll look into this later. It's still a bit tricky for me to fully understand its safety requirements.

Comment thread client/src/offset_allocator/bins_map.rs
Comment thread client/src/offset_allocator/mod.rs Outdated
free_nodes: FreeNodeStack<NI>,
}

struct NodeMap<NI: NodeIndex>(Vec<Node<NI>>);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was wrong with the stack? A slab is technically slightly more book-keeping.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe. I thought it was the ideal use case of a Slab, since it behaves as a key/value store for node indexes. I found the setup of the FreeNodeStack to be not ideal, especially since max_allocs is used to populate it.

I'll think more about this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't review this change closely enough to have an informed opinion about how it interacts with the other logic, since you recommended focusing elsewhere. If naturally providing a stable key simplifies code elsewhere compared to the stack, that would be a very good reason for the change.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with not looking into this too closely. To answer your question, though, the IDs were always stable. It just used to be implemented manually using two arrays. One of them was the one referenced with stable IDs, and the other acted as a stack and contained the full list of free IDs you could draw from. Its interface basically already matched the interface of a slab.

Because of that, when I was thinking of how to abstract that logic, the name of the data structure I wanted to use was "slab", and since we already depended on the slab crate, it seemed silly not to use that.

However, looking at the implementation of Slab, it seems like there might be a fair amount of memory overhead, as there's an enum with a usize as one of its options, which I think means that each slot takes at least 128 bits.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of them was the one referenced with stable IDs, and the other acted as a stack and contained the full list of free IDs you could draw from. Its interface basically already matched the interface of a slab.

Ah, that sounds like a fair reason to simplify with a real slab then! If upstream has a good reason not to do that, they should at least document it.

which I think means that each slot takes at least 128 bits.

This is unlikely to matter.

Comment thread client/src/graphics/png_array.rs Outdated
}

impl skid_steer::Source for PngArray {
type Output = DedicatedImage; // TODO: We may want a dedicated struct here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we need a dedicated struct?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left that comment there thinking I would be tripped up by having the asset loader return an image without any additional metadata, but I haven't reached that point yet.

I could imagine situations where the asset would want to retain additional data, such as the names of the materials it loaded. However, given Rust's type safety, I think that if that need arises, the refactor would be easy. Since we haven't needed a dedicated struct yet, I'll remove the comment.

Comment thread client/src/graphics/png_array.rs Outdated
Comment thread client/src/asset_loader.rs Outdated
Comment thread client/src/asset_loader.rs Outdated
Comment thread client/src/asset_loader.rs Outdated
Comment thread client/src/asset_loader.rs Outdated
align: usize,
free_at: u64,
) -> Allocation<'_> {
// TODO: Instead of blocking in this haphazard way, try growing the timeline ring instead if it's too small.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be necessary to avoid deadlocks in control flow paths that cross multiple allocations, which may occur in very difficult to reproduce ways with concurrent allocation traffic.

@patowen patowen Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I do agree it's worth redesigning that part.

Comment thread client/src/graphics/png_array.rs Outdated

impl PngArray {
async fn load_inner(self, context: &skid_steer::Context<'_>) -> anyhow::Result<DedicatedImage> {
println!("Started loading png array");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend getting comfortable with using tracing logs even (perhaps especially) for debugging, as it's easy to attach useful context to them via dynamic scope, and to include diagnostics that are normally inactive but don't need to be reinvented every time you're investigating an issue.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. It would be nice to not have to delete all the logs before submitting a PR, especially when they've proved to be useful in the past. I am pretty comfortable with using tracing logs, but I guess not comfortable enough to reach to them as a default.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may help to adopt the test-log crate, and ensure your development environment sets RUST_LOG to a useful configuration by default.

@patowen
patowen force-pushed the refactor-vulkan-wip branch from 87e2e61 to 223c79e Compare August 7, 2026 03:18
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.

2 participants