<p>I have managed to find the culprit:</p>

<pre><code>    std::vector&lt;vk::WriteDescriptorSet&gt; writeDescriptorSets;

    for (int i = 0; i &lt; swapchainImages.size(); ++i) {
        vk::DescriptorImageInfo descriptorImageInfo = {};
        descriptorImageInfo.imageView = *swapchainImageViews[i];
        descriptorImageInfo.imageLayout = vk::ImageLayout::eGeneral;

        vk::WriteDescriptorSet writeDescriptorSet = {};
        writeDescriptorSet.dstSet = descriptorSets[i];
        writeDescriptorSet.dstBinding = 0;
        writeDescriptorSet.descriptorCount = 1;
        writeDescriptorSet.descriptorType = vk::DescriptorType::eStorageImage;
        writeDescriptorSet.pImageInfo = &amp;descriptorImageInfo;
        writeDescriptorSets.push_back(writeDescriptorSet);
    }

    device-&gt;updateDescriptorSets((uint32_t)writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);
</code></pre>

<p>Do you see it yet?</p>

<p>What if I told you this fixed it:</p>

<pre><code>    std::vector&lt;vk::WriteDescriptorSet&gt; writeDescriptorSets;
    std::vector&lt;vk::DescriptorImageInfo&gt; descriptorImageInfos;

    for (int i = 0; i &lt; swapchainImages.size(); ++i) {
        vk::DescriptorImageInfo descriptorImageInfo = {};
        descriptorImageInfo.imageView = *swapchainImageViews[i];
        descriptorImageInfo.imageLayout = vk::ImageLayout::eGeneral;
        descriptorImageInfos.push_back(descriptorImageInfo);

        vk::WriteDescriptorSet writeDescriptorSet = {};
        writeDescriptorSet.dstSet = descriptorSets[i];
        writeDescriptorSet.dstBinding = 0;
        writeDescriptorSet.descriptorCount = 1;
        writeDescriptorSet.descriptorType = vk::DescriptorType::eStorageImage;
        writeDescriptorSet.pImageInfo = &amp;descriptorImageInfos[i];
        writeDescriptorSets.push_back(writeDescriptorSet);
    }

    device-&gt;updateDescriptorSets((uint32_t)writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);
</code></pre>

<p>vk::DescriptorImageInfo for the first two images was being overwritten before updateDescriptorSets was called.</p>

<p>Remember kids, when you could swear you're treating several things in the absolute same way, but they somehow behave differently (in most cases, only the last one is working), you're probably overwriting something somewhere, or something is going out of scope before you thought it would.</p>

CC BY-SA 4.0
2019-08-14T16:50:08.270Z
2019-08-15T10:20:37.777Z
2019-08-15T10:20:37.777Z
Karlovsky120
<p>I've managed to write a "small" Vulkan program that outputs a solid color using compute shader directly to swapchain.</p>

<p>However, it doesn't work properly. ImageMemoryBarriers I've setup don't transition the layouts the way they should and only every third swapchain image (it has three in my implementation) is actually rendered to.</p>

<p>Two most interesting functions are the one where I record the command buffers and the one where is submit them to the compute/present queue:</p>

<p>Recording:</p>

<pre><code>void createCommandBuffers() {
    commandBuffers.resize(swapchainImages.size());

    vk::CommandBufferAllocateInfo allocInfo = {};
    allocInfo.commandPool = commandPool.get();
    allocInfo.level = vk::CommandBufferLevel::ePrimary;
    allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();

    commandBuffers = device-&gt;allocateCommandBuffersUnique(allocInfo);

    for (size_t i = 0; i &lt; commandBuffers.size(); ++i) {
        vk::CommandBufferBeginInfo beginInfo = {};
        beginInfo.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse;

        commandBuffers[i]-&gt;begin(beginInfo);

        commandBuffers[i]-&gt;bindPipeline(vk::PipelineBindPoint::eCompute, *pipeline);
        commandBuffers[i]-&gt;bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipelineLayout, 0, descriptorSets[i], nullptr);

        vk::ImageMemoryBarrier imageMemoryBarrier = {};
        imageMemoryBarrier.oldLayout = vk::ImageLayout::eUndefined;
        imageMemoryBarrier.newLayout = vk::ImageLayout::eGeneral;
        imageMemoryBarrier.image = swapchainImages[i];
        imageMemoryBarrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
        imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
        imageMemoryBarrier.subresourceRange.levelCount = 1;
        imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
        imageMemoryBarrier.subresourceRange.layerCount = 1;

        commandBuffers[i]-&gt;pipelineBarrier(
            vk::PipelineStageFlagBits::eTopOfPipe,
            vk::PipelineStageFlagBits::eComputeShader,
            vk::DependencyFlagBits::eByRegion,
            nullptr,
            nullptr,
            imageMemoryBarrier);

        commandBuffers[i]-&gt;dispatch(WIDTH, HEIGHT, 1);

        imageMemoryBarrier = {};
        imageMemoryBarrier.oldLayout = vk::ImageLayout::eGeneral;
        imageMemoryBarrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
        imageMemoryBarrier.image = swapchainImages[i];
        imageMemoryBarrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
        imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
        imageMemoryBarrier.subresourceRange.levelCount = 1;
        imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
        imageMemoryBarrier.subresourceRange.layerCount = 1;

        commandBuffers[i]-&gt;pipelineBarrier(
            vk::PipelineStageFlagBits::eComputeShader,
            vk::PipelineStageFlagBits::eBottomOfPipe,
            vk::DependencyFlagBits::eByRegion,
            nullptr,
            nullptr,
            imageMemoryBarrier);

        commandBuffers[i]-&gt;end();
    }
}
</code></pre>

<p>Submitting:</p>

<pre><code>void drawFrame() {
    device-&gt;waitForFences(*inFlightFences[currentFrame], VK_TRUE, std::numeric_limits&lt;uint64_t&gt;::max());
    device-&gt;resetFences(*inFlightFences[currentFrame]);

    uint32_t imageIndex;

    try {
        device-&gt;acquireNextImageKHR(*swapchain, std::numeric_limits&lt;uint64_t&gt;::max(), *imageAvailableSemaphores[currentFrame], {}, &amp;imageIndex);
    }
    catch (vk::OutOfDateKHRError) {
        recreateSwapchain();
        return;
    }

    vk::SubmitInfo submitInfo = {};

    vk::Semaphore waitSemaphores[] = { *imageAvailableSemaphores[currentFrame] };
    submitInfo.waitSemaphoreCount = 1;
    submitInfo.pWaitSemaphores = &amp;waitSemaphores[0];

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &amp;*commandBuffers[imageIndex];

    vk::Semaphore signalSemaphores[] = { *renderFinishedSemaphores[currentFrame] };
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = &amp;signalSemaphores[0];

    vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eTopOfPipe };
    submitInfo.pWaitDstStageMask = waitStages;

    computeQueue.submit(submitInfo, *inFlightFences[currentFrame]);

    vk::PresentInfoKHR presentInfo = {};
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = &amp;signalSemaphores[0];

    vk::SwapchainKHR swapchains[] = { *swapchain };
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapchains;
    presentInfo.pImageIndices = &amp;imageIndex;

    try {
        vk::Result result = computeQueue.presentKHR(presentInfo);
        if (result == vk::Result::eSuboptimalKHR || framebufferResized) {
            framebufferResized = false;
            recreateSwapchain();
        }
    }
    catch (vk::OutOfDateKHRError) {
        framebufferResized = false;
        recreateSwapchain();
    }

    currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
</code></pre>

<p>The last function is basically the main loop at this point.</p>

<p>I've been staring at it for a while now and I don't get it. I don't know what is wrong. This is also the first Vulkan program I've written semi independently, so there's that as well.</p>

<p>Here's the rest of the code, if something is unclear: <a href="https://pastebin.com/bGS8Af6t" rel="nofollow noreferrer">pastebin</a></p>

<p>Also, for completeness sake, here's the shader, as simple as can be:</p>

<pre><code>#version 450

layout (binding = 0, rgba8) uniform image2D resultImage;

void main() {
    vec4 green = vec4(0.0, 1.0, 0.0, 1.0);

    imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), green);
}
</code></pre>

CC BY-SA 4.0
2019-08-13T22:15:48.337Z
Did you verify that your implementation allows you to use the swapchain image as a storage image?
Nicol Bolas
CC BY-SA 4.0
2019-08-13T22:21:27.700Z
Yeah, it allows. I've added push constants to the mix since I asked the question and am colouring each image of the swapchain the other colour to better see what's going on. Only image that is properly shown is the last swapchain image created, and the validation layers complain only about that image having the incorrect layout (the first barrier not working). Interestingly enough, if I change the first barrier to expect ePreinitialized layout, the program still works the same, but starts to complain about all of the swapchain images.
Karlovsky120
<vulkan><compute-shader><synchronization>
Only each third image in swapchain is being used, causing flickering
Karlovsky120
# Only each third image in swapchain is being used, causing flickering

(Asked by: username_0 on 2019-08-13)

I've managed to write a "small" Vulkan program that outputs a solid color using compute shader directly to swapchain.

However, it doesn't work properly. ImageMemoryBarriers I've setup don't transition the layouts the way they should and only every third swapchain image (it has three in my implementation) is actually rendered to.

Two most interesting functions are the one where I record the command buffers and the one where is submit them to the compute/present queue:

Recording:

```
void createCommandBuffers() {
    commandBuffers.resize(swapchainImages.size());

    vk::CommandBufferAllocateInfo allocInfo = {};
    allocInfo.commandPool = commandPool.get();
    allocInfo.level = vk::CommandBufferLevel::ePrimary;
    allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();

    commandBuffers = device->allocateCommandBuffersUnique(allocInfo);

    for (size_t i = 0; i < commandBuffers.size(); ++i) {
        vk::CommandBufferBeginInfo beginInfo = {};
        beginInfo.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse;

        commandBuffers[i]->begin(beginInfo);

        commandBuffers[i]->bindPipeline(vk::PipelineBindPoint::eCompute, *pipeline);
        commandBuffers[i]->bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipelineLayout, 0, descriptorSets[i], nullptr);

        vk::ImageMemoryBarrier imageMemoryBarrier = {};
        imageMemoryBarrier.oldLayout = vk::ImageLayout::eUndefined;
        imageMemoryBarrier.newLayout = vk::ImageLayout::eGeneral;
        imageMemoryBarrier.image = swapchainImages[i];
        imageMemoryBarrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
        imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
        imageMemoryBarrier.subresourceRange.levelCount = 1;
        imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
        imageMemoryBarrier.subresourceRange.layerCount = 1;

        commandBuffers[i]->pipelineBarrier(
            vk::PipelineStageFlagBits::eTopOfPipe,
            vk::PipelineStageFlagBits::eComputeShader,
            vk::DependencyFlagBits::eByRegion,
            nullptr,
            nullptr,
            imageMemoryBarrier);

        commandBuffers[i]->dispatch(WIDTH, HEIGHT, 1);

        imageMemoryBarrier = {};
        imageMemoryBarrier.oldLayout = vk::ImageLayout::eGeneral;
        imageMemoryBarrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
        imageMemoryBarrier.image = swapchainImages[i];
        imageMemoryBarrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
        imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
        imageMemoryBarrier.subresourceRange.levelCount = 1;
        imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
        imageMemoryBarrier.subresourceRange.layerCount = 1;

        commandBuffers[i]->pipelineBarrier(
            vk::PipelineStageFlagBits::eComputeShader,
            vk::PipelineStageFlagBits::eBottomOfPipe,
            vk::DependencyFlagBits::eByRegion,
            nullptr,
            nullptr,
            imageMemoryBarrier);

        commandBuffers[i]->end();
    }
}

```

Submitting:

```
void drawFrame() {
    device->waitForFences(*inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());
    device->resetFences(*inFlightFences[currentFrame]);

    uint32_t imageIndex;

    try {
        device->acquireNextImageKHR(*swapchain, std::numeric_limits<uint64_t>::max(), *imageAvailableSemaphores[currentFrame], {}, &imageIndex);
    }
    catch (vk::OutOfDateKHRError) {
        recreateSwapchain();
        return;
    }

    vk::SubmitInfo submitInfo = {};

    vk::Semaphore waitSemaphores[] = { *imageAvailableSemaphores[currentFrame] };
    submitInfo.waitSemaphoreCount = 1;
    submitInfo.pWaitSemaphores = &waitSemaphores[0];

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &*commandBuffers[imageIndex];

    vk::Semaphore signalSemaphores[] = { *renderFinishedSemaphores[currentFrame] };
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = &signalSemaphores[0];

    vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eTopOfPipe };
    submitInfo.pWaitDstStageMask = waitStages;

    computeQueue.submit(submitInfo, *inFlightFences[currentFrame]);

    vk::PresentInfoKHR presentInfo = {};
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = &signalSemaphores[0];

    vk::SwapchainKHR swapchains[] = { *swapchain };
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapchains;
    presentInfo.pImageIndices = &imageIndex;

    try {
        vk::Result result = computeQueue.presentKHR(presentInfo);
        if (result == vk::Result::eSuboptimalKHR || framebufferResized) {
            framebufferResized = false;
            recreateSwapchain();
        }
    }
    catch (vk::OutOfDateKHRError) {
        framebufferResized = false;
        recreateSwapchain();
    }

    currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}

```

The last function is basically the main loop at this point.

I've been staring at it for a while now and I don't get it. I don't know what is wrong. This is also the first Vulkan program I've written semi independently, so there's that as well.

Here's the rest of the code, if something is unclear: [pastebin](https://pastebin.com/bGS8Af6t)

Also, for completeness sake, here's the shader, as simple as can be:

```
#version 450

layout (binding = 0, rgba8) uniform image2D resultImage;

void main() {
    vec4 green = vec4(0.0, 1.0, 0.0, 1.0);

    imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), green);
}

```

**Tags:** vulkan, compute-shader, synchronization

> **Comments:**
> **username_1**: Did you verify that your implementation allows you to use the swapchain image as a storage image?  
> **username_0**: Yeah, it allows. I've added push constants to the mix since I asked the question and am colouring each image of the swapchain the other colour to better see what's going on. Only image that is properly shown is the last swapchain image created, and the validation layers complain only about that image having the incorrect layout (the first barrier not working). Interestingly enough, if I change the first barrier to expect ePreinitialized layout, the program still works the same, but starts to complain about all of the swapchain images.  

---

## 1 answers

### Answer by username_0 on 2019-08-14. Score: 0 | Accepted answer

I have managed to find the culprit:

```
    std::vector<vk::WriteDescriptorSet> writeDescriptorSets;

    for (int i = 0; i < swapchainImages.size(); ++i) {
        vk::DescriptorImageInfo descriptorImageInfo = {};
        descriptorImageInfo.imageView = *swapchainImageViews[i];
        descriptorImageInfo.imageLayout = vk::ImageLayout::eGeneral;

        vk::WriteDescriptorSet writeDescriptorSet = {};
        writeDescriptorSet.dstSet = descriptorSets[i];
        writeDescriptorSet.dstBinding = 0;
        writeDescriptorSet.descriptorCount = 1;
        writeDescriptorSet.descriptorType = vk::DescriptorType::eStorageImage;
        writeDescriptorSet.pImageInfo = &descriptorImageInfo;
        writeDescriptorSets.push_back(writeDescriptorSet);
    }

    device->updateDescriptorSets((uint32_t)writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);

```

Do you see it yet?

What if I told you this fixed it:

```
    std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
    std::vector<vk::DescriptorImageInfo> descriptorImageInfos;

    for (int i = 0; i < swapchainImages.size(); ++i) {
        vk::DescriptorImageInfo descriptorImageInfo = {};
        descriptorImageInfo.imageView = *swapchainImageViews[i];
        descriptorImageInfo.imageLayout = vk::ImageLayout::eGeneral;
        descriptorImageInfos.push_back(descriptorImageInfo);

        vk::WriteDescriptorSet writeDescriptorSet = {};
        writeDescriptorSet.dstSet = descriptorSets[i];
        writeDescriptorSet.dstBinding = 0;
        writeDescriptorSet.descriptorCount = 1;
        writeDescriptorSet.descriptorType = vk::DescriptorType::eStorageImage;
        writeDescriptorSet.pImageInfo = &descriptorImageInfos[i];
        writeDescriptorSets.push_back(writeDescriptorSet);
    }

    device->updateDescriptorSets((uint32_t)writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);

```

vk::DescriptorImageInfo for the first two images was being overwritten before updateDescriptorSets was called.

Remember kids, when you could swear you're treating several things in the absolute same way, but they somehow behave differently (in most cases, only the last one is working), you're probably overwriting something somewhere, or something is going out of scope before you thought it would.


---

