-
Notifications
You must be signed in to change notification settings - Fork 244
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
Implement ByteAddressableBuffer prototype detached from bindless #735
Conversation
I haven't thought about this much, but I could imagine that we treat these as unsafe, and the intention is that you use this functionality when it's really needed to implement some data structure on the GPU, and you're supposed to wrap it yourself to make it safe. This kind of thing is probably perfect for storing complicated things like bounding volume hierarchies where nodes and leaves are different structs, etc. |
My best guess for the problem of It claims it's UB to be in that situation but I haven't combed the standard for the exact reason why - OTOH we've seen this kind of UB driver-side (presumably because of the use of LLVM older than LLVM 12) on at least Nvidia/Windows, months ago. It's pretty old, so the only reasons we haven't noticed are:
|
Thanks for adding this back, though I have to say it feels weird to see this fully attributed to you now since most of this got introduced in #450 initially and subsequently removed by you in #710. It looks like some code got removed (support for
I agree and disagree with some of these and have some points to add on this as well. I think it's very rust-y to have unsafe (and even unsound) pieces of code, especially when related to this code, which is basically emulating the behavior of pointers to a certain extent. A feature that's pretty core to the foundations of Rust. Especially because this type is supposed to serve as an unsafe, low level abstraction on top of which safe(r) abstractions can be built. I think the name
This sounds conceptually quite close to for example Similarly, in #450 I added a few types that were safer to operate - for example a |
crates/rustc_codegen_spirv/src/builder/byte_addressable_buffer.rs
Outdated
Show resolved
Hide resolved
Co-authored-by: Markus Siglreithmaier <m.siglreith@gmail.com>
/// This can only happen in one specific case - which is as a result of | ||
/// `codegen_buffer_store_intrinsic`, that function is supposed to return | ||
/// OpTypeVoid, however because it gets inline by the compiler it can't. | ||
/// Instead we return this, and trigger an error if we ever end up using the | ||
/// result of this function call (which we can't). | ||
IllegalTypeUsed(Word), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand why this is needed, wouldn't an undef
work? I thought that was what was used in other similar cases. It's not that important though, feel free to merge without touching this at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah, probably, a zombie'd undef would work (I'm guessing you can't have an undef void) - this was directly carried over from the previous implementation, though, didn't really think about it that much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For context, there's some discussion around the original implementation here #450 (although quite a bit of discussion isn't actually in that PR, due to instead having calls due to frustration around PR stuff)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe what happens with the other cases is they make an undef
()
(i.e. the ZST Rust type, an OpTypeStruct
without fields), and OpTypeVoid
appears here only because it was intentionally specified.
This gives me an idea I could try (removing - tho because SpirvType::Void
and using Option
where necessary instead)rspirv
doesn't bother interning types, it would be unnecessarily inefficient.
/// Intrinsic for loading a <T> from a &[u32] | ||
pub buffer_load_intrinsic_fn_id: RefCell<FxHashSet<Word>>, | ||
/// Intrinsic for storing a <T> into a &[u32] | ||
pub buffer_store_intrinsic_fn_id: RefCell<FxHashSet<Word>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: this is a set (and not just a Cell<Option<_>>
) because it's recording SPIR-V IDs, not DefId
s, so all of the monomorphizations are different (even if they share one DefId
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yeah, thought about that when porting it over (was confused myself), really should have added a comment about that instead of copying stuff without thinking, haha
Tests are a little lacking, but definitely better than no tests at all.
Two problems with this:
panic!()
asOpUnreachable
(with some strange convoluted branching/looping surrounding it), which isn't correct (e.g. spirv-opt replaces it with a straight upOpReturn
)ByteAddressableBuffer
type isn't allowed directly in entrypoint arguments yet - that'll be cleaned up in the future (requires tangling entry.rs up a little more, and should probably be in a separate PR cleaning up/refactoring entry.rs as well). Converting&mut [u32]
<->ByteAddressableBuffer
will still be allowed, though, as that seems like a really useful feature.I do actually want to take a step back and discuss if this is actually needed and is in line with our goals of rust-gpu, though. This type is an incredibly type-unsafe, unsound, and not-rusty way of doing things, which seems to be against the goals of what rust-gpu wants to be (safe, sound, etc.). An example of an alternative way of doing this would be to make a "loadable from bytes" trait, implemented by us in spirv-std for all the basic types, and a proc macro(?) to automatically mark/implement it for structs (similar to serde). There's probably many other alternatives. So, I just want to step back and make sure we're on the right path here, and not just implementing things because it's what other shader languages do.