vkCmdClearColorImage does not clear the image

I have the following code to clear an image in vulkan

VkCommandBufferBeginInfo cbb = {};
cbb.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cbb.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

vkBeginCommandBuffer(cmdbuf, &cbb);

//Transition the image to "transfer optimal"
{
    VkImageMemoryBarrier imb = {};
    imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    imb.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    imb.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;              //we are going to copy into this image, so we don't care existing content
    imb.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    imb.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    imb.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    imb.image = _image->_vkimage;
    imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    imb.subresourceRange.baseMipLevel = 0;
    imb.subresourceRange.levelCount = 1;
    imb.subresourceRange.layerCount = 1;

    vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imb);
}

//Clear image
{
    VkClearColorValue ClearColorValue = { 1, 0, 0, 1 };

    VkImageSubresourceRange ImageSubresourceRange;
    ImageSubresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
    ImageSubresourceRange.baseMipLevel   = 0;
    ImageSubresourceRange.levelCount     = 1;
    ImageSubresourceRange.baseArrayLayer = 0;
    ImageSubresourceRange.layerCount     = 1;
    vkCmdClearColorImage(cmdbuf, _image->_vkimage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &ClearColorValue, 1, &ImageSubresourceRange);
}

//Transition the image back to original layout
{
    VkImageMemoryBarrier imb = {};
    imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    imb.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    imb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    imb.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    imb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    imb.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    imb.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    imb.image = _image->_vkimage;
    imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    imb.subresourceRange.baseMipLevel = mipLevelBase;
    imb.subresourceRange.levelCount = mipCount;
    imb.subresourceRange.layerCount = 1;

    vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imb);
}


vkEndCommandBuffer(cmdbuf);


//Submit
{
    VkSubmitInfo si = {};
    si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    si.commandBufferCount = 1;
    si.pCommandBuffers = &cmdbuf;

    VkResult res = vkQueueSubmit(_vxk._vk._queue, 1, &si, VK_NULL_HANDLE);
    if (res != VK_SUCCESS)
    {
        FIXME("vkQueueSubmit failed, result = %d", res);
        vkResetCommandBuffer(cmdbuf, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
        return ;
    }
}

vkDeviceWaitIdle(_vxk._vk._device);

But apparently the image is left untouched (unmodified) after this code. No error is reported by Vulkan or the validation layer. I can’t figure out what is going on…

I have similar code to write texels into the image and read them back. Those two work correctly and I use them to verify if the code above works.

Anybody has some hints or can explain what I’m doing wrong?

Thanks

Hello there.

I am not sure what the problem is here, but one thing that really simplified the issues our team had with image transitions was to create an abstraction which reduces code size in general. It looks something like this:

void Texture::transition_image_layout(VkImage image, VkFormat format, VkImageLayout old_layout,
                                  VkImageLayout new_layout) {

VkImageMemoryBarrier barrier = {};

barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = old_layout;
barrier.newLayout = new_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;

VkPipelineStageFlags source_stage = 0;
VkPipelineStageFlags destination_stage = 0;

if (VK_IMAGE_LAYOUT_UNDEFINED == old_layout && VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == new_layout) {
    barrier.srcAccessMask = 0;
    barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

    source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    destination_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == old_layout &&
           VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == new_layout) {
    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

    source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
    destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
    throw std::runtime_error("Error: unsupported layout transition!");
}

spdlog::debug("Recording pipeline barrier for image layer transition");

OnceCommandBuffer image_transition_change(device, data_transfer_queue, data_transfer_queue_family_index);

image_transition_change.create_command_buffer();
image_transition_change.start_recording();

vkCmdPipelineBarrier(image_transition_change.get_command_buffer(), source_stage, destination_stage, 0, 0, nullptr,
                     0, nullptr, 1, &barrier);

image_transition_change.end_recording_and_submit_command();
}

I am sure this could help you.

Best regards,
Johannes.

Hi, I’m answering to my own question to close it.
The problem was actually a missing synchronization and I was reading the image before it was written to.
So please consider this closed.