目录
- 先叨叨
- git信息
- 关键代码
- VulkanEnv::FindHostVisitbaleMemoryTypeIndex()
- TestPipeLine::CreateFramebuffers()
与网上大多数文章不同,其他文章基本上都使用窗口框架(X11、GLFW、WSL等)提供的surface来显示Vulkan渲染出的图像。我认为那样会屏蔽很多细节,因此我选择使用更原生的方式,即让Vulkan渲染到一块内存中,然后将内存读出再渲染到屏幕上。其实surface只不过是封装好的Image而以。
先叨叨
上一篇创建的RenderPass,但还没有给RenderPass分配内存空间。本篇来介绍如何给RenderPass创建内存空间。RenderPass与内存的对应关系如下图:
Vulkan的架构设计将RenderPass到Memeory的对应关系拉了一条很长的线路,至于为什么和这么设计的好处,我还理解不到。所以先死记硬背下来。
- RenderPass中有很多个Attachment每个,Attachment对应一块内存空间。Attachment用于指明该空间在渲染时具体起到的作用。如:颜色缓存、深度缓存、模板缓存等。
- 多个Attachment由一个Subpass进行关联,指明一次渲染会用到Subpass中的所有的Attachment。比如将第一个Attachment当作颜色缓存,将第二Attachment当作深度缓存。
- 一个RenderPass对应一个FrameBuffer。而FrameBuffer中有多个ImageView,每个ImageView对应一个RenderPass中的Attachment。。ImageView还不是真正的内存空间。
- ImageView会关联到一个Image。Image是对内存空间的描述,但Image并不是真正的内存空间。
- 真正的内存空间是Memory,Memory需要从Device上申请,申请完后需要绑定到Image上。
git信息
- repository: https://gitee.com/J8_series/easy-car-ui
- tag: 09-CreateFrameBuffer
- url: https://gitee.com/J8_series/easy-car-ui/tree/09-CreateFrameBuffer
关键代码
VulkanEnv::FindHostVisitbaleMemoryTypeIndex()
上面介绍了Memory需要从Device上申请,而Device可能有多个内存空间(堆)。我希望找到一个GPU和CPU都能访问的堆,因为我想把渲染完的图片拷贝出来。渲染需要GPU访问,而拷贝需要CPU访问。
void VulkanEnv::FindHostVisitbaleMemoryTypeIndex()
{
VkPhysicalDeviceMemoryProperties pMemoryProperties;
vkGetPhysicalDeviceMemoryProperties(m_selectedPhysicalDevice, &pMemoryProperties);
bool found = false;
for (uint32_t i = 0; i < pMemoryProperties.memoryTypeCount; ++i)
{
if (pMemoryProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
{
m_hostVisitbaleMemoryTypeIndex = i;
found = true;
break;
}
}
if (false == found)
{
throw std::runtime_error("To find host visiable memory is failed");
}
}
TestPipeLine::CreateFramebuffers()
本方法流程如下:
- 创建Image
- 申请Memory
- 将Image和Memory 绑定到一起
- 创建ImageView并关联到Image上
- 创建FrameBuffer。framebufferInfo.pAttachments的值是一个ImageView数组,数组里的元素顺序要与RenderPass中的Attachment顺序一致。Vulkan用这种方式实现了Attachment和ImageView的对应。
void TestPipeline::CreateFramebuffers()
{
//https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#VkImageCreateInfo
VkImageCreateInfo imageCreateInfo{};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.pNext = nullptr;
imageCreateInfo.flags;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UINT;
imageCreateInfo.extent = VkExtent3D{m_width, m_height, 1};
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imageCreateInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.queueFamilyIndexCount = 0;
imageCreateInfo.pQueueFamilyIndices = nullptr;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (VK_SUCCESS != vkCreateImage(m_device, &imageCreateInfo, nullptr, &m_image))
{
throw std::runtime_error("To create image is failed!");
}
// https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#VkMemoryAllocateInfo
VkMemoryAllocateInfo memoryAllocationInfo;
memoryAllocationInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAllocationInfo.pNext = nullptr;
memoryAllocationInfo.memoryTypeIndex = m_memroyTypeIndex;
memoryAllocationInfo.allocationSize = m_width * m_height * 4;
// https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#vkAllocateMemory
if (VK_SUCCESS != vkAllocateMemory(m_device, &memoryAllocationInfo, nullptr, &m_imageMemory))
{
throw std::runtime_error("To allocate memory is failed!");
}
// https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#vkBindImageMemory
if (VK_SUCCESS != vkBindImageMemory(m_device, m_image, m_imageMemory, 0))
{
throw std::runtime_error("To bind memory is failed!");
}
//https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#VkImageViewCreateInfo
VkImageViewCreateInfo imageViewCreateInfo{};
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imageViewCreateInfo.pNext = nullptr;
imageViewCreateInfo.flags = 0;
imageViewCreateInfo.image = m_image;
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
imageViewCreateInfo.format = VK_FORMAT_R8G8B8A8_UINT;
imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
imageViewCreateInfo.subresourceRange.levelCount = 1;
imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
imageViewCreateInfo.subresourceRange.layerCount = 1;
if (VK_SUCCESS != vkCreateImageView(m_device, &imageViewCreateInfo, nullptr, &m_imageView))
{
throw std::runtime_error("To create image view is failed!");
}
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = m_renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = &m_imageView;
framebufferInfo.width = m_width;
framebufferInfo.height = m_height;
framebufferInfo.layers = 1;
//https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#vkCreateFramebuffer
if (VK_SUCCESS != vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &m_framebuffer)) {
throw std::runtime_error("To create framebuffer is failed!");
}
}