Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate bind group layouts by value instead of "by ID" #335

Closed
kvark opened this issue Sep 9, 2019 · 6 comments · Fixed by #400 or #753
Closed

Validate bind group layouts by value instead of "by ID" #335

kvark opened this issue Sep 9, 2019 · 6 comments · Fixed by #400 or #753
Assignees
Labels
area: validation Issues related to validation, diagnostics, and error handling type: bug Something isn't working

Comments

@kvark
Copy link
Member

kvark commented Sep 9, 2019

The upstream WebGPU spec is written (currently in the discussion logs, not yet in the actual spec) in a way that makes two bind group layouts interchangeable as long as the content passed into their creation is exactly the same ("by value" semantics). This also matches all the low-level APIs behavior.

We need to change our validation rules to compare them by value instead of by ID.

@kvark kvark added type: bug Something isn't working area: validation Issues related to validation, diagnostics, and error handling labels Sep 9, 2019
@yanchith
Copy link
Contributor

yanchith commented Dec 4, 2019

I'd like to tackle this. I'll post updates/questions in this thread.

@yanchith
Copy link
Contributor

yanchith commented Dec 6, 2019

Okay, I think I've got a really rough version implemented. Since this relaxes the current constraints, I need to write some tests that to be more sure I didn't do something totally terrible - the current examples working doesn't prove anything. While implementing, I of course ran into things I'd like to understand a bit better:

The Hub Access DAG

I understand this is meant to prevent deadlocks by only allowing the programmer to access subsequent nodes in the graph, based on what was previously accessed, but I am unsure whether the current code enforces this. Because the registry methods don't consume the token and only take it by &mut, it is legal to use the same token twice (or even only use the root token always) and access various parts of the graph:

// This is not prevented currently
let (mut pass_guard, _) = hub.render_passes.write(&mut token);
// ... possibly somewhere else in code ...
let (mut pass_guard2, _) = hub.render_passes.write(&mut token);

I this could be made more difficult to accidentally violate by always consuming the access token, modifying its drop impl, and providing a method for transitioning a general token into a more specific one. The token would probably need to store the count by which to decrement the ACTIVE_TOKEN on drop, or something similar...

Bind Group Layout Compatibility

What does it mean for two Bind Group Layouts to be compatible? I currently assume they are compatible iff their BindGroupLayoutBindings vectors are equal, because everything else is just implementation structures derived from these descriptors. Am I missing something?

I also do have a couple of more specific, implementation questions:

  • Is it ok to derive PartialEq for BindGroupLayoutBinding or is it not implemented on purpose for future proofing?
  • To compare the bind group layouts by value, I need to get them to the places where the comparison happens. I noticed that usually in top-level code, the storages for various resources are acquired from the hub. Is it ok, if I propagate the storage for bind group layouts down the call stack? Or is there a more preferred way to do this?

I will publish a draft PR soon.

@kvark
Copy link
Member Author

kvark commented Dec 6, 2019

Because the registry methods don't consume the token and only take it by &mut, it is legal to use the same token twice (or even only use the root token always) and access various parts of the graph:

They borrow the token mutably, which means you can't have two storages accessed using the same token at the same time. In your example, either the first guard gets dropped before the second one is acquired, or the compiler will yell at you.

I currently assume they are compatible iff their BindGroupLayoutBindings vectors are equal, because everything else is just implementation structures derived from these descriptors. Am I missing something?

That's good for now. We can come back to this later.

Is it ok to derive PartialEq for BindGroupLayoutBinding or is it not implemented on purpose for future proofing?

Yes, it's ok and useful.

Is it ok, if I propagate the storage for bind group layouts down the call stack?

It sounds fine, we'll get to this when your PR is reviewed.

@yanchith
Copy link
Contributor

yanchith commented Dec 7, 2019

In your example, either the first guard gets dropped before the second one is acquired, or the compiler will yell at you.

I see your point and it indeed should prevent 2 borrows, but the current implementation doesn't. You can borrow two guards and deadlock with the current API, one doesn't even have to explicitly drop the tokens like I did in the above snippet.

I think this is because the input lifetime of the token is not tied to the output lifetime of either the token or the lock guard. I experimented by modifying the read and write functions a bit and managed to get at least the token lifetimes tied so that one really gets the borrow-checker error for invalid use.

However, even with my tweak, if you explicitly drop the tokens (but not the guards) returned from read and write, the original token will once again be unborrowed, leaving you free to deadlock 😄

I think this could be possible to solve on the type level, but I realize this is all really academical and only concerns an internal API, so I understand if we don't want to spend more time on this 😄

That's good for now. We can come back to this later.

Yeah, I just realized the BindGroupLayoutBinding for a bind group can specify a superset of shader stage visibility flags than what the BindGroupLayoutBinding for a pipeline specified, and they should still be compatible. We can improve this later.

@kvark
Copy link
Member Author

kvark commented Dec 8, 2019

@yanchith looks like you are on to something very important!
Please review the fix in #403

bors bot added a commit that referenced this issue Dec 16, 2019
400: Validate bind group layouts by value r=kvark a=yanchith

This PR widens the comparisons done between two bind group layouts to also consider them equivalent if their vectors of `BindGroupLayoutBinding`s are equal. 

Tested on various modifications of the cube example from wgpu-rs. https://gist.github.com/yanchith/56e15fe35658b14587ff9bfcbd53116f

Fixes #335 

Co-authored-by: yanchith <yanchi.toth@gmail.com>
@bors bors bot closed this as completed in 6a96389 Dec 17, 2019
@kvark
Copy link
Member Author

kvark commented Mar 25, 2020

We had to disable that for now:

if false {

@kvark kvark reopened this Mar 25, 2020
bors bot added a commit that referenced this issue Jun 27, 2020
753: Deduplicate BindGroupLayout by value r=cwfitzgerald a=kvark

**Connections**
Fixes #335 

**Description**
BGL now has distinct `MultiRefCount` type with a bit of a hacky logic to support deduplication.
Good review is needed!

**Testing**
Ran wgpu-rs examples.
The actual new logic is NOT tested enough!

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
@bors bors bot closed this as completed in 2547c43 Jun 27, 2020
kvark added a commit to kvark/wgpu that referenced this issue Jun 3, 2021
* Enable X11 in Vulkan

* Fix Cargo comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: validation Issues related to validation, diagnostics, and error handling type: bug Something isn't working
Projects
None yet
2 participants