I created a storage texture and stored the same fragments that is displayed to the screen. But when I read from the same texture in another pipeline's fragment shader it doesn't seem to be working properly...
The Code uses the same render pass and same pipeline layout. I start the render pass and go through the first pipeline then end it and do a vkCmdPipelineBarrier for the storage image texture, then start the same render pass again and do the second pipeline, then end the render pass.
I don't know where I made a mistake?
This is what I was expecting but this is what I got. And its also flickering.
here is the vkCmdPipelineBarrier after the first end render pass and before the second begin render pass call...
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
// We won't be changing the layout of the image
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.image = outlineTexture->getTeximage();
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
Here is the texture creation process...
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(creDevice.physical_Device(), VK_FORMAT_R8G8B8A8_UNORM, &formatProperties);
// Check if requested image format supports image storage operations
assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
VkImageCreateInfo imageCreateInfo{};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageCreateInfo.extent = { (uint32_t)texWidth, (uint32_t)texHeight, 1 };
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
// Image will be sampled in the fragment shader and used as storage target in the compute shader
imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
imageCreateInfo.flags = 0;
// If compute and graphics queue family indices differ, we create an image that can be shared between them
// This can result in worse performance than exclusive sharing mode, but save some synchronization to keep the sample simple
/*
std::vector<uint32_t> queueFamilyIndices;
if (vulkanDevice->queueFamilyIndices.graphics != vulkanDevice->queueFamilyIndices.compute) {
queueFamilyIndices = {
vulkanDevice->queueFamilyIndices.graphics,
vulkanDevice->queueFamilyIndices.compute
};
imageCreateInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
imageCreateInfo.queueFamilyIndexCount = 2;
imageCreateInfo.pQueueFamilyIndices = queueFamilyIndices.data();
}
*/
QueueFamilyIndices indices = creDevice.findPhysicalQueueFamilies();
uint32_t queueFamilyIndices[] = { indices.graphicsFamily, indices.presentFamily };
if (indices.graphicsFamily != indices.presentFamily) {
imageCreateInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
imageCreateInfo.queueFamilyIndexCount = 2;
imageCreateInfo.pQueueFamilyIndices = queueFamilyIndices;
}
else {
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.queueFamilyIndexCount = 0; // Optional
imageCreateInfo.pQueueFamilyIndices = nullptr; // Optional
}
//imageCreateInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;
//imageCreateInfo.queueFamilyIndexCount = 2;
//imageCreateInfo.pQueueFamilyIndices = queueFamilyIndices;
creDevice.createImageWithInfo(imageCreateInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
if (auto commandBuffer = creDevice.beginSingleTimeCommands()) {
VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.layerCount = 1;
VkImageMemoryBarrier imageMemoryBarrier{};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.image = textureImage;
imageMemoryBarrier.subresourceRange = subresourceRange;
imageMemoryBarrier.srcAccessMask = 0;
vkCmdPipelineBarrier(
commandBuffer,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
creDevice.endSingleTimeCommands(commandBuffer);
}
// Create sampler
VkSamplerCreateInfo sampler{};
sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler.magFilter = VK_FILTER_LINEAR;
sampler.minFilter = VK_FILTER_LINEAR;
sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
sampler.addressModeV = sampler.addressModeU;
sampler.addressModeW = sampler.addressModeU;
sampler.mipLodBias = 0.0f;
sampler.maxAnisotropy = 1.0f;
sampler.compareOp = VK_COMPARE_OP_NEVER;
sampler.minLod = 0.0f;
sampler.maxLod = creDevice.msaaSamples;
//sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
if (vkCreateSampler(creDevice.device(), &sampler, nullptr, &textureSampler) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture sampler!");
};
// Create image view
VkImageViewCreateInfo view{};
view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view.image = VK_NULL_HANDLE;
view.viewType = VK_IMAGE_VIEW_TYPE_2D;
view.format = VK_FORMAT_R8G8B8A8_UNORM;
view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
view.image = textureImage;
if (vkCreateImageView(creDevice.device(), &view, nullptr, &textureImageView) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture image view!");
}
And here is the store to texture shader...
#version 450
layout (location = 0) in vec3 fragColor;
layout (location = 1) in vec2 screenSize;
layout (set = 0, binding = 2, rgba8) uniform image2D otlImage;
layout (set = 1, binding = 1) uniform sampler2D texSampler;
layout (location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0) * texture(texSampler, fragTextureCoord);
imageStore(otlImage, ivec2((gl_FragCoord.x + 0.5)/screenSize.x, (gl_FragCoord.y + 0.5)/screenSize.y), outColor);
}
And here is the reading from the same texture Part...
"""Vertex shader"""
#version 450
layout (location = 0) out vec2 outUV;
void main()
{
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
}
"""Fragment Shader"""
#version 450
layout (location = 0) in vec2 outUV;
layout (location = 0) out vec4 outColor;
layout (set = 0, binding = 2, rgba8) uniform image2D otlImage;
void main()
{
outColor = imageLoad(otlImage, ivec2(outUV));
}
You need to adjust your image sampling positions. You seem to pass UV coordinates in the 0..1 range from your vertex to your fragment shader and use these for your image loads. But unlike image sampling, image loads expect image coordinates in texel space. So e.g. if your image has a dimension of 512x512 and you want to sample from the center, you'd need to use ivec(256, 256) for your image load.