I am trying to post process image with compute shader, and use previous frame's color in the current computations. I need sampler2D to access interpolation operations, even though it doesn't seem like so in the following simple example. I update current in-out 'color' with following shader:
#version 460
#extension GL_GOOGLE_include_directive : enable
layout (local_size_x = 16, local_size_y = 16) in;
layout(binding = 0, set = 0, rgba32f) uniform image2D color;
layout(binding = 1, set = 0) uniform sampler2D previous_color;
void main()
{
ivec2 xy_int = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(color);
vec2 xy_float = vec2(gl_GlobalInvocationID.x / size.x, gl_GlobalInvocationID.y / size.y);
vec3 sample_color = imageLoad(color, xy_int).xyz;
// Frame 2 and onwards
vec3 prev_color = texture(previous_color, xy_float).xyz;
vec3 mixed_color = mix(sample_color, prev_color, 0.5);
imageStore(color, vec4(mixed_color, 1.0f), xy_int);
}
After dispatching:
- I transition the image layouts using pipeline barriers
- Use vkCmdCopyImage to copy the 'color' image to 'previous_color'
- Transition back the layout to be ready to be used in the next frame
All of this (including the compute shader dispatch) is done in one command buffer. However, inspecting the 'previous_color' as input texture with Renderdoc, I see that the texture is always black and doesn't exist. I can also inspect the copy event, which shows that the copy was successful and 'previous_color' image has the correct color.
Am I approaching copying wrong here? Why is the copied image not available as texture the next frame? I am not getting any Vulkan validation layer errors with my setup.
I assume something is messed up with my synchronization, but cannot find out what. I am not using queue families. More specifically, my layout transitions are:
vkBeginCommandBuffer()
...
vkCmdDispatch() // the example_shader.comp above
...
color ImageMemoryBarrier with:
access flags: VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT -> VK_ACCESS_TRANSFER_READ_BIT
layout transition: VK_IMAGE_LAYOUT_GENERAL -> VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
stage: VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT -> VK_PIPELINE_STAGE_TRANSFER_BIT
prev_color ImageMemoryBarrier with:
access flags: VK_ACCESS_SHADER_READ_BIT -> VK_ACCESS_TRANSFER_WRITE_BIT
layout transition: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL -> VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
stage: VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT -> VK_PIPELINE_STAGE_TRANSFER_BIT
vkCmdCopyImage(color, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, previous_color, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
prev_color ImageMemoryBarrier with:
access flags: VK_ACCESS_TRANSFER_WRITE_BIT -> VK_ACCESS_SHADER_READ_BIT
layout transition: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL -> VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
stage: VK_PIPELINE_STAGE_TRANSFER_BIT -> VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
color ImageMemoryBarrier with:
access flags: VK_ACCESS_TRANSFER_READ_BIT -> VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
layout transition: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL -> VK_IMAGE_LAYOUT_GENERAL
stage: VK_PIPELINE_STAGE_TRANSFER_BIT -> VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
...
vkEndCommandBuffer()
The Sampler2D was created with:
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 0;
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
EDIT: I didn't realize that the 'previous_color' is actually usable now! There might actually not be any errors other than a bug in Renderdoc, not displaying the previous_color as input texture as black. This requires little further exploration, as I did change barriers to be more strict than before. I was only looking with renderdoc, without realizing the end result was what I was looking for already.
The best way to confirm synchronization issues is to temporarily add more synchronization to force serial execution. If it starts working then it's a sync issue.
If it's still black it's not a sync issue.