I hope that I'm able to explain my problem as clear as possible. I'm working on a Rust application that uses Vulkan.
I ran into some trouble creating the graphics pipeline. I found the issue, but I have no idea what and why it is happening.
First let me show what works. For creating the graphics pipelines I need to create something called a PipelineViewportStateCreateInfo
. This struct (and some others) I then use to create a GraphicsPipelineCreateInfo
.
The width
and height
properties of swapchain_extent are set to 1600 and 1200 respectively.
let viewports = [Viewport::builder()
.x(0.0)
.y(0.0)
.width(swapchain_extent.width as f32)
.height(swapchain_extent.height as f32)
.min_depth(0.0)
.max_depth(0.0)
.build()];
let scissors = [Rect2D::builder()
.offset(Offset2D::builder().x(0).y(0).build())
.extent(swapchain_extent)
.build()];
let viewport_state_create_info = PipelineViewportStateCreateInfo::builder()
.scissors(&scissors)
.viewports(&viewports)
.build();
let graphics_pipeline_create_infos = [GraphicsPipelineCreateInfo::builder()
.viewport_state(&viewport_state_create_info)
.build()];
This all works perfectly.
To make my code more structured I created a helper function that takes care of creating the PipelineViewPortStateCreateInfo
:
fn create_viewport_state_create_info(extent: Extent2D) -> PipelineViewportStateCreateInfo {
let viewports = [Viewport::builder()
.x(0.0)
.y(0.0)
.width(extent.width as f32)
.height(extent.height as f32)
.min_depth(0.0)
.max_depth(0.0)
.build()];
let scissors = [Rect2D::builder()
.offset(Offset2D::builder().x(0).y(0).build())
.extent(extent)
.build()];
PipelineViewportStateCreateInfo::builder()
.viewports(&viewports)
.scissors(&scissors)
.build()
}
And I call it from my other function like this:
let viewport_state_create_info = create_viewport_state_create_info(swapchain_extent);
let graphics_pipeline_create_infos = [GraphicsPipelineCreateInfo::builder()
.viewport_state(&viewport_state_create_info)
.build()];
I don't see a problem with this code. However when I run this code the Vulkan validation layer throws this error back at me:
VALIDATION [VUID-VkViewport-width-01770 (-1542042715)]: Validation Error: [ VUID-VkViewport-width-01770 ] | MessageID = 0xa4164ba5 | vkCreateGraphicsPipelines(): pCreateInfos[0].pViewportState->pViewports[0].width (0.000000) is not greater than 0.0. The Vulkan spec states: width must be greater than 0.0
Even though swapchain_extent.width
is set to 1600.
In the debugger I see that it is indeed 1600, but when I step into the build function of the GraphicsPipelineCreateInfoBuilder
suddenly both width and height become 0.0 🤔
Is this some scoping issue? If it is, isn't the compiler supposed to protect me against it?
Yes, you are correct in your assumption. It is a scoping issue.
The issue is that while PipelineViewportStateCreateInfoBuilder
is bound by 'a
and references viewports
and scissors
. The issue is that PipelineViewportStateCreateInfo
does not, and instead keeps pointers (not references) to viewports
and scissors
.
#[repr(C)]
pub struct PipelineViewportStateCreateInfo {
...
pub p_viewports: *const Viewport,
pub p_scissors: *const Rect2D,
...
}
So the second that create_viewport_state_create_info()
returns, then both viewports
and scissors
are dropped. So the pointers in PipelineViewportStateCreateInfo
are now invalid.
The reason that the compiler isn't able to catch this. Is because when build()
is called, it returns a PipelineViewportStateCreateInfo
which is not bound by any lifetime anymore. It could in theory. However, ash
is simply mirroring the definition of VkPipelineViewportStateCreateInfo
.
The PipelineViewportStateCreateInfoBuilder
is actually doing exactly what you would do in that case. It contains a PipelineViewportStateCreateInfo
and uses a PhantomData
for the unused lifetime.
#[repr(transparent)]
pub struct PipelineViewportStateCreateInfoBuilder<'a> {
inner: PipelineViewportStateCreateInfo,
marker: ::std::marker::PhantomData<&'a ()>,
}