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

[WIP] Implement extension chaining #183

Merged
merged 21 commits into from
Mar 10, 2019
Merged

[WIP] Implement extension chaining #183

merged 21 commits into from
Mar 10, 2019

Conversation

MaikKlein
Copy link
Member

This is only a proof on concept right now. I'll finish it up next week.

What are your thoughts? Right now this requires a cast from *const to *mut.

pub struct ExtensionChainMut {
pub s_type: StructureType,
pub p_next: *mut c_void
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Dead code?

pub struct ExtensionChain {
pub s_type: StructureType,
pub p_next: *mut c_void
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this be named BaseStructure or similar, along the lines of upstream?

Copy link
Member Author

Choose a reason for hiding this comment

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

There is also vk::BaseOutStructure which we might just reuse.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That makes sense; then the loop helper can just be a top-level function, which perhaps shouldn't even be exported; it doesn't seem very broadly useful, and reducing API surface area is nice.

unsafe{
let ptr = &mut self.inner as *mut _ as *mut c_void;
let last_extension = ExtensionChain::last_chain(ptr);
(*last_extension).p_next = next.deref_mut() as *mut T as *mut c_void;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I kinda like the idea of prepending to the chain rather than appending, since it means we don't have to rely on the optimizer to erase the loop. On the other hand, the resulting sequence of links is less intuitive, and I'm not certain that the ordering never matters.

@Ralith
Copy link
Collaborator

Ralith commented Feb 28, 2019

Difficult to review this due to the mangled history. Rebase?

@MaikKlein MaikKlein force-pushed the extension-structs branch 2 times, most recently from 5b427dd to b5257bf Compare February 28, 2019 09:58
use std::ops::Deref;
println!("--");
println!("{:?}", corner.deref() as *const _);
println!("{:?}", variable_pointers.deref() as *const _);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we still need these prints?

Copy link
Member Author

Choose a reason for hiding this comment

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

No I'll convert this code to a test. Not quite sure where to put it though. Possibly into the vk.rs.

unsafe{
let last_extension = ptr_chain_iter(&mut self.inner)
.last()
.expect("Initial ptr was null");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we get rid of ptr_chain_iter too, now, and keep the API narrower?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to use it for tests. Maybe we can just define it inside the test module.

.expect("Initial ptr was null");
// Append the extension at the end
(*last_extension).p_next = next as *mut T as *mut _;
let next_ptr: *mut BaseOutStructure = ::std::mem::transmute(next);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Transmute isn't needed for casting between pointers.


let next_function = if has_next {
let next_extends = _struct.extends.as_ref().map(|extends| {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This variable name is confusing. Maybe something like extension_trait_name?

let next_extends = _struct.extends.as_ref().map(|extends| {
let extends = extends
.split(',')
.find(|extend| root_create_info_names.contains(&extend.to_string()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

When is this check necessary? Do there exist structs that extend non-root structs?

Copy link
Member Author

Choose a reason for hiding this comment

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

Do there exist structs that extend non-root structs?

Yes not all extend structs are root. I'll add a comment, and I to slightly rewrite this anyway.

.split(',')
.find(|extend| root_create_info_names.contains(&extend.to_string()))
.expect("Should have a root create info");
name_to_tokens(&format!("Extends{}", name_to_tokens(&extends)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we be implementing the extension trait of every struct that this struct extends, rather than just an arbitrary one?

Copy link
Member Author

Choose a reason for hiding this comment

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

What do you mean by arbitrary one? Only root create infos will create a new trait, and only non root create infos that extend a root create info will implement this trait.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unless I'm missing something, this seems to be used for deciding when to implement the trait, not when to define it. A single extension struct (e.g. VkPhysicalDeviceVariablePointerFeatures) can extend multiple root structs, and must implement all of their extension traits.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yes that is what I wanted to implement. I just chose the first one.

@@ -1613,7 +1612,9 @@ pub fn derive_setters(
// implement
let next_trait = if has_next && _struct.extends.is_none() {
quote! {
pub unsafe trait #extends_name {}
pub unsafe trait #extends_name {
unsafe fn as_ptr_mut(&mut self) -> *mut BaseOutStructure;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of generating more code here, we should mark all builders as #[repr(transparent)] and coerce the pointers directly in push_next. This is legal because the builders contain only one non-zero-sized member.

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a great idea, I completely forgot about #[repr(transparent)].

@Ralith
Copy link
Collaborator

Ralith commented Mar 2, 2019

We shouldn't be referring to extensible root structs as "create infos," because not every extensible struct is named SomethingCreateInfo. For example, consider output structs like VkPhysicalDeviceFeatures2.

@MaikKlein
Copy link
Member Author

@Ralith I think I addressed all issues.

We shouldn't be referring to extensible root structs as "create infos," because not every extensible struct is named SomethingCreateInfo. For example, consider output structs like VkPhysicalDeviceFeatures2.

I don't think that I am? I think I only refer to root create infos if they are structs that are passed into a function.

@MaikKlein MaikKlein requested a review from Ralith March 4, 2019 09:56

// We only implement a next methods for root create infos
let next_function = if has_next && next_extends.is_none() {
let next_function = if has_next && root_extends.is_empty() {
quote! {
/// Prepends the given extension struct between the root and the first pointer. This
/// method only exists on create infos that can be passed to a function directly. Only
Copy link
Collaborator

Choose a reason for hiding this comment

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

This method only exists on create infos that can be passed to a function directly

This is incorrect, because this method exists (and is useful) on output structs too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you have an example?

Copy link
Collaborator

Choose a reason for hiding this comment

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

For example, it should be (but seemingly is not yet) defined for VkPhysicalDeviceProperties2. The same goes for any other root output struct with a p_next member.

Copy link
Member Author

Choose a reason for hiding this comment

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

The current implementation doesn't allow for pointer chains to be added. As in push_next on VkPhysicalDeviceProperties2 and then insert VkPhysicalDeviceProperties2 into DeviceCreateInfo. We would need to iterate over the chains (which is fine). I don't think this is necessary though. push_next only on root create infos seems fine to me.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand what you're trying to say. VkPhysicalDeviceProperties2 is an output struct; it does not extend VkDeviceCreateInfo, it is used to return data from vkGetPhysicalDeviceProperties2. It, and every struct like it, needs exactly the same extension semantics as you've implemented so far for root CreateInfo structs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay I see what you mean. It is correctly implemented (as in VkPhysicalDeviceProperties2 has a push_next fn). I'll just have to update the comments.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, so it is, was just searching badly.

}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this need to be in the generated code, rather than a regular module of the ash crate?

Copy link
Member Author

Choose a reason for hiding this comment

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

We could have this code outside of the generator, and if we have ash-sys it will then be part of that crate. I'll change it.

.iter()
.filter_map(|definition| match *definition {
vkxml::DefinitionsElement::Struct(ref _struct) => {
let is_root_create_info = _struct.extends.is_none();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is incorrect for VkPhysicalDeviceProperties2, which is both a root getter struct and an extension struct for CreateInfo

Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead, why don't we use the set of things that appears in structextends attributes? I haven't seen any cases where those are layered, though maybe I haven't looked hard enough.

Copy link
Member Author

Choose a reason for hiding this comment

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

which is both a root getter struct and an extension struct for CreateInfo

What exactly do you mean by that? Afaik it is just used in vkGetPhysicalDeviceProperties2.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Disregard, I was confused; too early for code review...

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't worry happens to me all the time :).

@MaikKlein
Copy link
Member Author

Right now the only thing that is missing are better variable names and fixing some comments right?

@Ralith
Copy link
Collaborator

Ralith commented Mar 9, 2019

There's still the test code in the generator that doesn't need to be there.

@MaikKlein
Copy link
Member Author

I believe we should still iterate through the chains even if it not necessary for the builder.(Attach head and tail) You can still create pointer chains without the builders and inserting those would not work. Otherwise we could implement ExtendXXX only for builders but I don't think that would be better.

@Ralith
Copy link
Collaborator

Ralith commented Mar 9, 2019

How about Iterating through the chain of the argument to push_next instead of self? That way we preserve everything, but in the overwhelmingly common case of single structs being supplied at a time, no extra work needs to be done.

@MaikKlein
Copy link
Member Author

How about Iterating through the chain of the argument to push_next instead of self? That way we preserve everything, but in the overwhelmingly common case of single structs being supplied at a time, no extra work needs to be done.

Yeah that was the idea :).

@MaikKlein MaikKlein force-pushed the extension-structs branch from e61bfd6 to c8253cf Compare March 9, 2019 18:35
@MaikKlein MaikKlein force-pushed the extension-structs branch from c8253cf to 1744159 Compare March 9, 2019 18:51
@MaikKlein MaikKlein requested a review from Ralith March 9, 2019 21:10
@@ -2144,6 +2198,23 @@ pub fn write_source_code(path: &Path) {
let source_code = quote! {
use std::fmt;
use std::os::raw::*;
/// Iterates through the pointer chain. Includes the item that is passed into the function.
/// Stops at the last `BaseOutStructure` that has a null `p_next` field.
pub unsafe fn ptr_chain_iter<T>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a good reason for this to be part of the public API?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is used in the test api. I am not sure, is it even possible to share pub(crate) code in the external tests module? I could move the tests into lib.rs, and it should work with use super::vk.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That sounds like a better solution to me. You could also have an internal tests module, rather than cramming everything into lib.rs.

We should be trying to minimize the amount of code the generator emits (because it's inconvenient to manipulate) and the amount of public API that doesn't have a specific strong motivation (for ease of maintenance).

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed, a lot of the trait / macro definitions could be defined outside of the generator.

@@ -657,11 +657,18 @@ impl<'a> ::std::ops::Deref for PhysicalDeviceWaveLimitPropertiesAmdBuilder<'a> {
}
}
impl<'a> PhysicalDeviceWaveLimitPropertiesAmdBuilder<'a> {
pub fn next<T>(mut self, next: &'a mut T) -> PhysicalDeviceWaveLimitPropertiesAmdBuilder<'a>
pub fn push_next<T>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm confused about where this definition is coming from. It's missing the chain support added to the generated version. I guess it's hand-written? Is that necessary?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes it is handwritten. I am not sure what we should do about it. Maybe we should remove all the builder stuff because it will probably get out of date anyway?

I am going to update it anyway.

Everything in experimental won't adhere to semver, so we can remove some parts in the future. I probably should document that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not a blocker, then, but why isn't this generated?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh this is not included in the vk.xml. That is why it is inside the experimental module. @msiglreith basically reversed the API :). This will be deleted when it is officially included in the vk.xml.

Copy link
Collaborator

@Ralith Ralith left a comment

Choose a reason for hiding this comment

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

Remaining issues aren't stability-sensitive, and can be fixed in future work.

@@ -657,11 +657,18 @@ impl<'a> ::std::ops::Deref for PhysicalDeviceWaveLimitPropertiesAmdBuilder<'a> {
}
}
impl<'a> PhysicalDeviceWaveLimitPropertiesAmdBuilder<'a> {
pub fn next<T>(mut self, next: &'a mut T) -> PhysicalDeviceWaveLimitPropertiesAmdBuilder<'a>
pub fn push_next<T>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not a blocker, then, but why isn't this generated?

@@ -2144,6 +2198,23 @@ pub fn write_source_code(path: &Path) {
let source_code = quote! {
use std::fmt;
use std::os::raw::*;
/// Iterates through the pointer chain. Includes the item that is passed into the function.
/// Stops at the last `BaseOutStructure` that has a null `p_next` field.
pub(crate) unsafe fn ptr_chain_iter<T>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Because it's static code, this should probably be defined in ash/lib.rs, not spliced into the generator output.

@MaikKlein
Copy link
Member Author

bors r+

bors bot added a commit that referenced this pull request Mar 10, 2019
183: [WIP] Implement extension chaining r=MaikKlein a=MaikKlein

This is only a proof on concept right now. I'll finish it up next week.

What are your thoughts? Right now this requires a cast from `*const` to `*mut`.

Co-authored-by: Maik Klein <[email protected]>
@bors
Copy link
Contributor

bors bot commented Mar 10, 2019

@bors bors bot merged commit cbc96b2 into master Mar 10, 2019
@MarijnS95 MarijnS95 deleted the extension-structs branch April 13, 2021 09:21
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