ash icon indicating copy to clipboard operation
ash copied to clipboard

Add FramebufferCreateInfoBuilder::attachment_count

Open kvark opened this issue 4 years ago • 15 comments

Currently, it only has attachments, which require the actual slice to be passed in. However, in the image-less framebuffer scenario, only the count needs to be set. So the builder isn't compatible with this right now.

kvark avatar Jun 16 '21 04:06 kvark

I've just been passing &[vk::ImageView::null(); N].

Ralith avatar Jun 17 '21 00:06 Ralith

This works if you know the size at run-time. Obviously, there are workarounds, but I think the builder pattern has this ability in other places to specify the count without providing a slice, and here it's just an omission.

kvark avatar Jun 17 '21 05:06 kvark

but I think the builder pattern has this ability in other places to specify the count without providing a slice, and here it's just an omission.

@kvark Are you referring to other places within Ash, and if so, have an example? As far as I'm aware vk.xml annotates all array fields with the right length field so these should be uncommon if not inexsistent. In that sense this is not an intentional omission either, as it is usually invalid to set the size to some non-zero value while still passing a NULL pointer.

This works if you know the size at run-time

I think you meant compile-time? For runtime you can use the somewhat ugly heap allocation through &vec![vk::ImageView::null(); runtime_value].

MarijnS95 avatar Jun 17 '21 12:06 MarijnS95

Spec says that pAttachments field is ignored for imageless framebuffer, but attachmentCount field must still be filled with non zero value in that case.

nickkuk avatar Jun 17 '21 12:06 nickkuk

@nickkuk Correct. "pAttachments... If flags includes VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, this parameter is ignored." is why the above workaround works.

MarijnS95 avatar Jun 17 '21 12:06 MarijnS95

Yes, and now to create imageless framebuffer I use builder pattern like this:

    let mut framebuffer_create_info = vk::FramebufferCreateInfo::builder()
      .flags(vk::FramebufferCreateFlags::IMAGELESS)
      .render_pass(render_pass.vk_render_pass)
      .width(extent.width)
      .height(extent.height)
      .layers(1)
      .push_next(&mut framebuffer_attachments_create_info)
      .build();
    framebuffer_create_info.attachment_count = attachment_configs.len() as u32;

With @kvark's suggestion it will be

    let framebuffer_create_info = vk::FramebufferCreateInfo::builder()
      .flags(vk::FramebufferCreateFlags::IMAGELESS)
      .render_pass(render_pass.vk_render_pass)
      .width(extent.width)
      .height(extent.height)
      .layers(1)
      .push_next(&mut framebuffer_attachments_create_info)
      .attachment_count(attachment_configs.len() as u32)
      .build();

without mutability and ugly heap allocation.

nickkuk avatar Jun 17 '21 12:06 nickkuk

@nickkuk The effect of this suggestion is well-understood. I'm just saying that this isn't an intentional omission to make life hard, because it is generally "unsafe" to manually change counts without passing an appropriate slice-pointer, that is why the builders do not have these functions. An exception can probably be made for this struct, I'd accept such a PR.

MarijnS95 avatar Jun 17 '21 12:06 MarijnS95

General advice: After implementing the suggestion, please don't call .build() unless absolutely necessary. Passing it to vkCreateFramebuffer auto-derefs the builder, without loosing lifetime info on the pointer(s) contained within VkFramebufferCreateInfo (pNext).

MarijnS95 avatar Jun 17 '21 12:06 MarijnS95

Sorry, @MarijnS95, I misunderstood your question. Another place where I had to set some count manually was vk::WriteDescriptorSet's field descriptor_count to bind VkWriteDescriptorSetAccelerationStructureKHR (I use nvidia extension for now, don't know about whether such manual set is needed for KHR).

nickkuk avatar Jun 17 '21 12:06 nickkuk

General advice: After implementing the suggestion, please don't call .build() unless absolutely necessary.

@MarijnS95, yes, in my first imageless framebuffer example I actually forget that DerefMut works for changing fields without .build() too (as you suggested for vkCreateFramebuffer).

nickkuk avatar Jun 17 '21 13:06 nickkuk

Indeed vk::WriteDescriptorSet is one of these other weird cases where descriptor_count is shared across 3 array-pointers (CC #445, that might actually break this) and the array in VkWriteDescriptorSetAccelerationStructureKHR in the pNext field.

MarijnS95 avatar Jun 17 '21 13:06 MarijnS95

I looked through the other usages of count, and indeed descriptor_count may be the only non-trivial one. So we can consider attachment_count somewhat unique. The suggestion to do &vec![vk::ImageView::null(); runtime_value] is very dangerous since it breaks if one calls build() in unexpected ways. Overall, ash is still a fully unsafe API, so providing attachment_count isn't going to be anything extraordinary. I suppose you just don't want to make an exception for this, which is a reasonable concern.

kvark avatar Jun 17 '21 14:06 kvark

The suggestion to do &vec![vk::ImageView::null(); runtime_value] is very dangerous since it breaks if one calls build() in unexpected ways.

It is exactly the same as borrowing the temporary array (unless making it static or const):

error[E0716]: temporary value dropped while borrowed
  --> examples/src/bin/texture.rs:91:35
   |
91 |                     .attachments(&[vk::ImageView::null(); 3])
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
92 |                     .layers(1);
   |                               - temporary value is freed at the end of this statement
...
95 |                     .create_framebuffer(&frame_buffer_create_info, None)
   |                                         ------------------------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

Both are useless except if creating the object/builder within the same expression as create_framebuffer.

I suppose you just don't want to make an exception for this

I very well want to make an exception for this as mentioned above, I just don't want to make it the default for other builders where slice-length and slice-pointer are completely tied together. Regardless of general unsafety, Ash builders try to hold ones hand to the best of our abilities when pointer-lifetime is concerned.

MarijnS95 avatar Jun 17 '21 14:06 MarijnS95

Instead of exposing this for all the builders, which will be rather useless, maybe we can make this one more special, like

builder.imageless_attachment_count(4)

which would set the attachment_count and add the imageless flag at the same time?

kvark avatar Jun 17 '21 15:06 kvark

Spec says that pAttachments field is ignored for imageless framebuffer, but attachmentCount field must still be filled with non zero value in that case.

I came across a very similar situation, in my case it were the scissorCount (and viewportCount) fields in the VkPipelineViewportStateCreateInfo struct. when using dynamic viewport state, the pViewPorts pointer is ignored, but the viewportCcount is always required. luckily I got a validation layer report.

interestingly, when I go back and fix the code, the PipelineViewportStateCreateInfoBuilder struct actually already has these builder/setter methods: scissor_count(self, u32) -> Self and viewport_count(self, u32) -> Self, which is surprising to me, as I don't recall seeing other struct with similar situation.

I looked through the other usages of count, and indeed descriptor_count may be the only non-trivial one. So we can consider attachment_count somewhat unique. [...]

after some digging, I found it was added in #143, where several special cases (including the scissor_count and viewport_count) were defined, so this attachment_count could just be another one, I think?

nerditation avatar Dec 07 '21 08:12 nerditation