Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render to texture in Vulkan?

Tags:

c++

vulkan

I need render to textrue in my project.

Pass1 : Draw a color square that color is (0.5,0.5,0.5,1.0) and render target is a texture.

Pass2 : Draw a textured square that use pass1's texture and render target is a surface.

Expected result:

enter image description here

But I got strange result as follow:

enter image description here

Create image using the following:

  VkImage hImageTexture = VK_NULL_HANDLE;
  VkImageCreateInfo imageCreateInfo = {0};
  imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  imageCreateInfo.pNext = nullptr;
  imageCreateInfo.flags = 0;
  imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
  imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
  imageCreateInfo.extent.width = width;
  imageCreateInfo.extent.height = height;
  imageCreateInfo.extent.depth = 1;
  imageCreateInfo.mipLevels = 1;
  imageCreateInfo.arrayLayers = 1;
  imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
  imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
  imageCreateInfo.usage =
    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
    VK_IMAGE_USAGE_TRANSFER_DST_BIT |
    VK_IMAGE_USAGE_SAMPLED_BIT;
  imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  imageCreateInfo.queueFamilyIndexCount = 0;
  imageCreateInfo.pQueueFamilyIndices = nullptr;
  imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  //
  VkResult res = vkCreateImage(hDevice , &imageCreateInfo , nullptr , &hImageTexture);

Create image view using the following:

  VkImageView hImageViewTexture = VK_NULL_HANDLE;
  VkImageViewCreateInfo imageViewCreateInfo = {0};
  imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  imageViewCreateInfo.pNext = nullptr;
  imageViewCreateInfo.flags = 0;
  imageViewCreateInfo.image = hImageTexture;
  imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
  imageViewCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;
  imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_R;
  imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_G;
  imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_B;
  imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_A;
  imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
  imageViewCreateInfo.subresourceRange.levelCount = 1;
  imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
  imageViewCreateInfo.subresourceRange.layerCount = 1;
  VkResult res=vkCreateImageView(
    hDevice,
    &imageViewCreateInfo,
    NULL,
    &hImageViewTexture);

Render loop as follows:

  //Pass1
  //Clear texture  color
  vkCmdPipelineBarrier();//Transition layout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  vkCmdClearColorImage();//Clear color(0.0 , 0.0 ,0.0 , 1.0)
  vkCmdPipelineBarrier();//Transition layout to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
  //Change texture's barrier
  VkImageMemoryBarrier imageMemoryBarrierForOutput = {0};
  imageMemoryBarrierForOutput.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  imageMemoryBarrierForOutput.pNext = nullptr;
  imageMemoryBarrierForOutput.srcAccessMask = 0;
  imageMemoryBarrierForOutput.dstAccessMask = 
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  imageMemoryBarrierForOutput.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  imageMemoryBarrierForOutput.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  imageMemoryBarrierForOutput.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  imageMemoryBarrierForOutput.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  imageMemoryBarrierForOutput.image = hImageTexture;
  imageMemoryBarrierForOutput.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  imageMemoryBarrierForOutput.subresourceRange.baseMipLevel = 0;
  imageMemoryBarrierForOutput.subresourceRange.levelCount = 1;
  imageMemoryBarrierForOutput.subresourceRange.baseArrayLayer = 0;
  imageMemoryBarrierForOutput.subresourceRange.layerCount = 1;
  vkCmdPipelineBarrier(
    hCommandBuffer,
    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
    VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
    0,
    0,
    nullptr,
    0,
    nullptr,
    1,
    &imageMemoryBarrierForOutput);
  //draw
  vkCmdBeginRenderPass();
  vkCmdSetViewport();
  vkCmdSetScissor();
  vkCmdBindPipeline();
  vkCmdBindDescriptorSets();
  vkCmdBindVertexBuffers();
  vkCmdBindIndexBuffer();
  vkCmdDrawIndexed();
  vkCmdEndRenderPass();
  //
  //Pass2
  //Clear surface color
  vkCmdPipelineBarrier();//Transition layout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
  vkCmdClearColorImage();//Clear color(0.5 , 0.5 ,1.0 , 1.0)
  vkCmdPipelineBarrier();//Transition layout to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
  //Change texture's barrier
  VkImageMemoryBarrier imageMemoryBarrierForInput = {0};
  imageMemoryBarrierForInput.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  imageMemoryBarrierForInput.pNext = nullptr;
  imageMemoryBarrierForInput.srcAccessMask = 0;
  imageMemoryBarrierForInput.dstAccessMask = 
    VK_ACCESS_SHADER_READ_BIT |
    VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
  imageMemoryBarrierForInput.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  imageMemoryBarrierForInput.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  imageMemoryBarrierForInput.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  imageMemoryBarrierForInput.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  imageMemoryBarrierForInput.image = hImageTexture;
  imageMemoryBarrierForInput.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  imageMemoryBarrierForInput.subresourceRange.baseMipLevel = 0;
  imageMemoryBarrierForInput.subresourceRange.levelCount = 1;
  imageMemoryBarrierForInput.subresourceRange.baseArrayLayer = 0;
  imageMemoryBarrierForInput.subresourceRange.layerCount = 1;
  vkCmdPipelineBarrier(
    hCommandBuffer,
    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
    VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
    0,
    0,
    nullptr,
    0,
    nullptr,
    1,
    &imageMemoryBarrierForInput);
  //draw
  vkCmdBeginRenderPass();
  vkCmdSetViewport();
  vkCmdSetScissor();
  vkCmdBindPipeline();
  vkCmdBindDescriptorSets();
  vkCmdBindVertexBuffers();
  vkCmdBindIndexBuffer();
  vkCmdDrawIndexed();
  vkCmdEndRenderPass();

Pass1 vertex shader:

#version 450

layout (location=0) in vec4 inPos;
layout (location=0) out vec4 outPos;

void main(void)
{
  outPos = float4(inPos.xy , 0.0 , 1.0);  
  gl_Position = outPos;
}

Pass1 fragment shader:

#version 450

layout (location=0) in vec4 inPos;
layout (location=0) out vec4 outColor;

void main(void)
{
  outColor = float4(0.5 , 0.5 , 0.5 , 1.0);    
}

Pass2 vertex shader:

#version 450

layout (location=0) in vec4 inPos;
layout (location=1) in vec2 inUV;
//
layout (location=0) out vec4 outPos;
layout (location=1) out vec2 outUV;
//
void main(void)
{
  outPos = inPos;
  outUV = inUV;
  //
  gl_Position=outPos;
}

Pass2 fragment shader:

#version 450
//
layout (location=0) in vec4 inPos;
layout (location=1) in vec2 inUV;
//
layout (binding=1) uniform sampler2D inTex;
layout (location=0) out vec4 outColor;
void main(void)
{
  outColor = texture(inTex , inUV);
}

If I change image tiling to VK_IMAGE_TILING_LINEAR then I get the following:

enter image description here

If change image tiling to VK_IMAGE_TILING_LINEAR without clear(pass1's clear), the result was correct!

What mistakes have I made?

EDIT: I add some code that how i change texture's barrier in render loop .

EDIT: I set dependency when create render pass as follow

  VkSubpassDependency subpassDependency[2];
  subpassDependency[0].srcSubpass = VK_SUBPASS_EXTERNAL;
  subpassDependency[0].dstSubpass = 0;
  subpassDependency[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
  subpassDependency[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
  subpassDependency[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  subpassDependency[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
  //
  subpassDependency[1].srcSubpass = 0;
  subpassDependency[1].dstSubpass = VK_SUBPASS_EXTERNAL;
  subpassDependency[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  subpassDependency[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
  subpassDependency[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  subpassDependency[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
  subpassDependency[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

but nothing to changed.

like image 533
Hosee Avatar asked Oct 23 '25 20:10

Hosee


1 Answers

What you have here is a graphics-graphics dependency on the first pass output color attachment.
The second subpass shader read operations have to wait for the first subpass color attachment writing operations to be done.

 VkSubpassDependency deps[...];
  //other values to handle external dependencies
  ...
  // the src operations will refer to the first subpass
  deps[0].srcSubpass = 0; 
  // the dst operations will refer to the second subpass 
  deps[0].dstSubpass = 1;
  // the operations from the first subpass that will have to be completed
  // the writes to the color attachment in the color attachment output stage 
  deps[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  deps[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  // this operations will wait on those specified in the previous lines
  // the reads issued during the fragment shader will have to wait 
  // until all the writes have been completed 
  deps[0].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
  deps[0].dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
  // do not wait for every write to have finished
  // if all writes in a region have been completed
  // then the reads can start (only in that region) 
  deps[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;

This takes care of the synchronization between the subpasses and the changes of layout in one frame as the subpasses specify the layouts of the attachments.
Then you need to signal to the pipeline that you intend to move to the next render pass.

  //draw
  vkCmdBeginRenderPass();

  vkCmdSetViewport();
  vkCmdSetScissor();
  vkCmdBindPipeline();
  vkCmdBindDescriptorSets();
  vkCmdBindVertexBuffers();
  vkCmdBindIndexBuffer();
  vkCmdDrawIndexed();
  
  vkNextSubpass();
  
  vkCmdSetViewport();
  vkCmdSetScissor();
  vkCmdBindPipeline();
  vkCmdBindDescriptorSets();
  vkCmdBindVertexBuffers();
  vkCmdBindIndexBuffer();
  vkCmdDrawIndexed();

  vkCmdEndRenderPass();

For examples of common synchronization operations you can check out the vulkan synchronization examples page.
Your problem is First draw writes to a color attachment. Second draw reads from it as an input attachment in the fragment shader as you are doing everything in one render pass by dividing it in more subpasses.

like image 162
Kevin Spaghetti Avatar answered Oct 25 '25 10:10

Kevin Spaghetti



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!