-
Notifications
You must be signed in to change notification settings - Fork 192
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
WIP: Implement builder-like setters #117
Conversation
Ooh, great idea! Not being able to ergonomically supply slices to structs was always disappointing. As written, however, I am worried that this is a bit of a landmine. In your example code, I believe the array literals are short-lived--their lifetime ends after the I'd also suggest stripping the |
I assume that something like this should work
This should then look like
Thoughts? |
That doesn't help; the compile error you're seeing is unrelated. |
This should work and users can still store ii with Alternatively we could just accept it as it is, deref happens inside Vulkan and those functions are already marked as unsafe. |
But this hides raw pointers behind references. |
Can't |
This only works for structs with one variant.
I am not sure why PhantomData would be unsafe in FFI? Where is this documented? I always assumed that it is just ignored in codegen? |
I assume you mean only one field. But that's not the case. Example from the RFC: // Transparent struct wrapper with a marker.
#[repr(transparent)]
struct TransparentWrapper<T> {
only_non_zero_sized_field: f64,
marker: PhantomData<T>,
} The non-zero-sized field could be the actual structure we need to pass to Vulkan. The builder wraps that structure and adds a PhantomData marker. |
That would be another possibility
Edit: I guess we could also just implement
|
That also avoids needing to deal with impact on other traits, not to mention complicating every vulkan function with the addition of lifetime arguments; I like it.
I think this is preferable, because it doesn't interfere with lifetime checking. |
It would be great if builder could check that all fields are setup with valid data. EDIT. Maybe this is not the job for ash but for higher level code. |
@omni-viral I think this is best suited for layers / validation layers. |
I'd say yes; after all, these methods may set multiple fields (e.g. a length field too), and it leaves the door open for other higher-level settlers in the future.
I think this is exactly the sort of zero-cost helper Ash should have. There's discussion of exposing a bindings-only sys crate at #93 for when that's not desired. |
Yes I think that would make sense.
Yes this fits Ash perfectly. |
210a03b
to
4a6e51f
Compare
It doesn't seem useful to me to have |
I only do a partial review for the generated code for now. I'll look at it more closely tomorrow. Some thoughts: All the count setters are kind of useless. You might be able to filter them out. I am not sure if there are any edge cases. For example a field with the count name that is not used for an array. I think you can check for the substring in the names For example
I think this could become
I haven't yet thought about all the implications yet. I guess it could protect you from very stupid things but the lifetime is only tied to the builder and so it might appear more safe than it actually is. But the builder also using borrows for slices so this would make it more consistent. |
While using this branch, I've noticed that inside |
Hm that does seem quite tricky to properly implement. I guess we could add escape hatches for specific types where we could manually implement some of these setters. |
It should be straightforward to filter these out by never generating setters for anything referenced by a
This isn't absolutely necessary. For example, you could detect when a single length field is used by multiple pointers, and generate whatever preferred implementation in that case. I'm not sure what the preferred implementation for this case would be, though... |
Maybe just an |
A |
generator/src/lib.rs
Outdated
let count_members: Vec<String> = members.clone().filter_map(|field| { | ||
if field.array.is_some() { | ||
if let Some(ref array_size) = field.size { | ||
if !array_size.starts_with("latexmath") && array_size.ends_with("Count") { |
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.
What invalid cases are excluded by the ends_with("Count")
constraint?
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.
That's a failure in my brain. Thanks for catching that.
It seems in C99 type punning is fine if done through unions[1]. In any case, is there any user-friendly way to create shader module with SPIR-V obtained from include_bytes aside from converting it to vector of proper alignment? [1] https://stackoverflow.com/questions/25664848/unions-and-type-punning |
You don't need to worry about alignment, either it is valid in the first place or it is not. You can use I am not sure what the best API for the builder would be. |
That's why you should worry.
No, you can't. |
That is not the job of ash. As a user you need to check this yourself and make sure the SPIR-V that you load is valid.
That is not really a helpful comment at all. Why not?
Surely you can read it as bytes and cast it to u32? Why do you think that would be illegal? |
I think Rust doesn't have a perfect way of describing a type that is a binary blob with a certain alignment. It doesn't have to consist of only |
It's better to make the unsafety obvious than to silently perform such a conversion internally.
The convention established by Vulkan here is to treat it as a buffer of |
But even in Vulkan, Maybe the correct way is to use |
It's definitely weird and awkward, and if there was authoritative information that the alignment implications of the pointer type could be ignored then I'd go for that in a heartbeat. |
I think The PR looks fine to me, if there are no objections I am going to merge it tomorrow. And @MatusT thank you for all the hard work. |
if !array_size.starts_with("latexmath") { | ||
let length_type; | ||
let array_size_ident = Ident::from(array_size.to_snake_case().as_str()); | ||
if array_size_ident.to_string().contains("_count") { |
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.
length_type seems unnecessary. I think you can just cast with self.foo = foo.len() as _
, I'll address it myself
|
One problem with |
@kocsis1david VkPipelineViewportStateCreateInfo.viewportCount: now this is quite a problem. Because it was agreed that we filter out TL;DR: if there are no more of these cases => treat them as special cases and let's add manual overrides similar to pCode and pCodeSize. @MaikKlein @omni-viral @Ralith Sounds ok? |
One more thing is |
I think you meant |
Automatically generating everything according to general rules is never quite going to work out right; a small proportion of special cases is expected and absolutely fine. |
BTW, I hope you don't mind if I write my findings here or is there a better place? |
@kocsis1david It is okay, at least everything is still in one place. |
So far I fixed:
Should I open additional PRs for builder fixes? |
The Bool32 -> bool is implemented as well. |
Builders for types with
|
That's a pretty cool idea, but I don't think it should block getting the badly needed next release out the door. |
@omni-viral Seems doable I looked at the vk.xml
I think we can rely on |
@Ralith thanks. It's just an idea. It isn't blocking. I just figured it is good place to write it down 😄 |
@MatusT Yes PRs would be appreciated. Lets open separate issues, Github starts to get slow. |
This PR implements builder-like setters in order to reduce the noise created by manual filling of Vulkan's structs. It is supposed to work nicely with the implementation of Default trait and further improve it. The main contribution is in more rustier versions of p_ and pp_ fields, now taking Rust's slices and filling appropriate _count or _size field automatically.
Example of code before:
And example of code after applying builder-like pattern.
Please let me know your opinion on this in the comments.
EDIT:
Implementation based on the PhantomData and Deref is in the PR. Things to resolve:
This change is