Can't execute command on swapchain image
Hello,
When I try to execute a command directly on a swapchain image, I got a layout error:
thread '<unnamed>' panicked at 'Can't execute clear command buffer: AccessError { error: ImageNotInitialized { requested: PresentSrc }, command_name: "vkCmdCopyImageToBuffer", command_param: "source", command_offset: 0 }', libcore/result.rs:945:5
With a command like this one:
let cb = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), queue.family(),).unwrap()
.copy_image_to_buffer(
swapchain_images[image_num].clone(),
buffer.clone()
)
.unwrap()
.build()
.unwrap();
I got the same error with the clear_color_image command.
I searched in the API but I can't find a way to manually update the layout. Why the AutoCommandBufferBuilder doesn't expose the vkCmdPipelineBarrier function ?
Thanks, JS
Why the AutoCommandBufferBuilder doesn't expose the vkCmdPipelineBarrier function ?
Per design, the AutoCommandBufferBuilder is supposed to automatically handle pipeline barriers for you.
Any updates on this?
Or put differently: How can I initialize a swapchain image? I'd like to render a stream from my webcam after processing it with a compute shader straight to a window. Missing the straight to a window part, but I guess I can copy it back to ram and then push it through a graphics pipeline with the framebuffer/renderpass stuff
Just want to add that I'm running into this exact issue - specifically, using an AutoCommandBufferBuilder with only the clear_color_image command on a swapchain image.
I can verify that something to do with .clear_color_image is definitely causing the issue as the AccessError no longer occurs when I remove the call.
#974 seems very similar, though runs into the issue with a StorageImage rather than a SwapchainImage. That said, it's definitely a helpful read for digging into this.
Both the initial_layout_requirement and final_layout_requirement are expected to be PresentSrc, however vkCmdClearColorImage requires either TransferDstOptimal or General. Seeing as it seems it is the role of SyncCommandBufferBuilder::prev_cmd_resource to handle these layout transitions, I'm going to look into this here first.
@realitix did you ever work out a solution? I noticed you mentioned you were going to try the solution mentioned in #974, any luck?
Memory barrier image layouts seem OK
Memory barriers are inserted via the add_image_memory_barrier method. In the simple clear_color_image command buffer I describe in my previous comment, add_image_memory_barrier gets called twice:
- When calling the
clear_color_imagebuilder. - When calling
build.
The first call is a barrier with layout transition from PresentSrc -> TransferDstOptimal so that the image may be cleared. The second is a transition from TransferDstOptimal -> PresentSrc so that it is ready for present. If I print out the current_layout and new_layout within the add_image_memory_barrier it seems to be called correctly with both layout transitions, so it seems my incorrect-image-layout-theory is a dead-end.
Actual Err result trace
The exact place from which the error is returned is via the following trace:
GpuFuture::then_executeCommandBuffer::execute_afterSyncCommandBuffer::lock_submit(viaCommandBuffer::lock_submitimpl)SwapchainAcquireFuture::check_image_access(viaFuture::check_image_access)
The SwapchainAcquireFuture::check_image_access impl looks like this:
#[inline]
fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, _: bool, _: &Queue)
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError> {
let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap();
if swapchain_image.image.internal_object() != image.inner().image.internal_object() {
return Err(AccessCheckError::Unknown);
}
if self.swapchain.images[self.image_id]
.undefined_layout
.load(Ordering::Relaxed) && layout != ImageLayout::Undefined
{
println!("err a");
return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized {
requested: layout,
}));
}
if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc {
println!("err b");
return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout {
allowed: ImageLayout::PresentSrc,
requested: layout,
}));
}
Ok(None)
}
Specifically, it's the first condition that is met and that is that branch which returns the error.
The thing I find strange about the first condition is that:
self.swapchain.images[self.image_id]
.undefined_layout
.load(Ordering::Relaxed)
is returning true in order to get into this branch, however from my understanding the swapchain image does have a defined layout (PresentSrc) so I would have expected this to return false. If I comment out that first error condition, my program seems to run correctly downstream (the window clears with the correct colour) which supports the possibility that undefined_layout might be incorrect.
I will see if I can investigate further into this undefined_layout field.
It looks like undefined_layout is initialised to true within Swapchain::new_inner, but a quick
grep -nr undefined_layout ./vulkano/src/
shows that it can never be changed from this. The only other time the field seems to get read is within the SwapchainAcquireImage::check_image_access which I mentioned is the source of the Err in my previous comment.
This leads me to believe that the source of the problem is either:
-
Swapchain images do initially start with
ImageLayout::Undefinedbut are eventually changed toImageLayout::PresentSrcandundefined_layoutshould be changed tofalsewhen this happens but currently is not or -
Swapchain images always initialise with
ImageLayout::PresentSrcand this field is unnecessary, meaning that the error branch I'm currently hitting is unnecessary.
Will do some investigating into this.
@mitchmindtree Thanks for your investigation. I use Ash now, that let me more liberty.