Revising Vulkan example to address leak
parent
c714ee5249
commit
504473bcaf
@ -551,6 +551,25 @@ Add at the top of the Vulkan example main.cpp file after the `check_vk_result()`
|
|||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
// A struct to manage data related to one image in vulkan
|
||||||
|
struct MyTextureData
|
||||||
|
{
|
||||||
|
VkDescriptorSet DS; // Descriptor set: this is what you'll pass to Image()
|
||||||
|
int Width;
|
||||||
|
int Height;
|
||||||
|
int Channels;
|
||||||
|
|
||||||
|
// Need to keep track of these to properly cleanup
|
||||||
|
VkImageView ImageView;
|
||||||
|
VkImage Image;
|
||||||
|
VkDeviceMemory ImageMemory;
|
||||||
|
VkSampler Sampler;
|
||||||
|
VkBuffer UploadBuffer;
|
||||||
|
VkDeviceMemory UploadBufferMemory;
|
||||||
|
|
||||||
|
MyTextureData() { memset(this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
// Helper function to find Vulkan memory type bits. See ImGui_ImplVulkan_MemoryType() in imgui_impl_vulkan.cpp
|
// Helper function to find Vulkan memory type bits. See ImGui_ImplVulkan_MemoryType() in imgui_impl_vulkan.cpp
|
||||||
uint32_t findMemoryType(uint32_t type_filter, VkMemoryPropertyFlags properties)
|
uint32_t findMemoryType(uint32_t type_filter, VkMemoryPropertyFlags properties)
|
||||||
{
|
{
|
||||||
@ -568,12 +587,12 @@ uint32_t findMemoryType(uint32_t type_filter, VkMemoryPropertyFlags properties)
|
|||||||
return 0xFFFFFFFF; // Unable to find memoryType
|
return 0xFFFFFFFF; // Unable to find memoryType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to load an image with common settings and return a VkDescriptorSet as a sort of Vulkan pointer
|
// Helper function to load an image with common settings and return a MyTextureData with a VkDescriptorSet as a sort of Vulkan pointer
|
||||||
bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* image_width, int* image_height)
|
bool LoadTextureFromFile(const char* filename, MyTextureData* tex_data)
|
||||||
{
|
{
|
||||||
// Specifying 4 channels forces stb to load the image in RGBA which is an easy format for Vulkan
|
// Specifying 4 channels forces stb to load the image in RGBA which is an easy format for Vulkan
|
||||||
int image_channels = 4;
|
tex_data->Channels = 4;
|
||||||
unsigned char* image_data = stbi_load(filename, image_width, image_height, 0, image_channels);
|
unsigned char* image_data = stbi_load(filename, &tex_data->Width, &tex_data->Height, 0, tex_data->Channels);
|
||||||
|
|
||||||
if (image_data == NULL)
|
if (image_data == NULL)
|
||||||
{
|
{
|
||||||
@ -581,20 +600,18 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate allocation size (in number of bytes)
|
// Calculate allocation size (in number of bytes)
|
||||||
size_t image_size = (*image_width)*(*image_height)*image_channels;
|
size_t image_size = tex_data->Width*tex_data->Height*tex_data->Channels;
|
||||||
|
|
||||||
VkResult err;
|
VkResult err;
|
||||||
|
|
||||||
// Create the Vulkan image.
|
// Create the Vulkan image.
|
||||||
VkImage texture_image;
|
|
||||||
VkDeviceMemory texture_image_memory;
|
|
||||||
{
|
{
|
||||||
VkImageCreateInfo info = {};
|
VkImageCreateInfo info = {};
|
||||||
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
info.imageType = VK_IMAGE_TYPE_2D;
|
info.imageType = VK_IMAGE_TYPE_2D;
|
||||||
info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
info.extent.width = *image_width;
|
info.extent.width = tex_data->Width;
|
||||||
info.extent.height = *image_height;
|
info.extent.height = tex_data->Height;
|
||||||
info.extent.depth = 1;
|
info.extent.depth = 1;
|
||||||
info.mipLevels = 1;
|
info.mipLevels = 1;
|
||||||
info.arrayLayers = 1;
|
info.arrayLayers = 1;
|
||||||
@ -603,37 +620,35 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
err = vkCreateImage(g_Device, &info, g_Allocator, &texture_image);
|
err = vkCreateImage(g_Device, &info, g_Allocator, &tex_data->Image);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
VkMemoryRequirements req;
|
VkMemoryRequirements req;
|
||||||
vkGetImageMemoryRequirements(g_Device, texture_image, &req);
|
vkGetImageMemoryRequirements(g_Device, tex_data->Image, &req);
|
||||||
VkMemoryAllocateInfo alloc_info = {};
|
VkMemoryAllocateInfo alloc_info = {};
|
||||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
alloc_info.allocationSize = req.size;
|
alloc_info.allocationSize = req.size;
|
||||||
alloc_info.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
alloc_info.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &texture_image_memory);
|
err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &tex_data->ImageMemory);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
err = vkBindImageMemory(g_Device, texture_image, texture_image_memory, 0);
|
err = vkBindImageMemory(g_Device, tex_data->Image, tex_data->ImageMemory, 0);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the Image View
|
// Create the Image View
|
||||||
VkImageView image_view;
|
|
||||||
{
|
{
|
||||||
VkImageViewCreateInfo info = {};
|
VkImageViewCreateInfo info = {};
|
||||||
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
info.image = texture_image;
|
info.image = tex_data->Image;
|
||||||
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
info.subresourceRange.levelCount = 1;
|
info.subresourceRange.levelCount = 1;
|
||||||
info.subresourceRange.layerCount = 1;
|
info.subresourceRange.layerCount = 1;
|
||||||
err = vkCreateImageView(g_Device, &info, g_Allocator, &image_view);
|
err = vkCreateImageView(g_Device, &info, g_Allocator, &tex_data->ImageView);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Sampler
|
// Create Sampler
|
||||||
VkSampler sampler;
|
|
||||||
{
|
{
|
||||||
VkSamplerCreateInfo sampler_info{};
|
VkSamplerCreateInfo sampler_info{};
|
||||||
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||||
@ -646,49 +661,47 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
sampler_info.minLod = -1000;
|
sampler_info.minLod = -1000;
|
||||||
sampler_info.maxLod = 1000;
|
sampler_info.maxLod = 1000;
|
||||||
sampler_info.maxAnisotropy = 1.0f;
|
sampler_info.maxAnisotropy = 1.0f;
|
||||||
err = vkCreateSampler(g_Device, &sampler_info, g_Allocator, &sampler);
|
err = vkCreateSampler(g_Device, &sampler_info, g_Allocator, &tex_data->Sampler);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Descriptor Set using ImGUI's implementation
|
// Create Descriptor Set using ImGUI's implementation
|
||||||
*img_ds = ImGui_ImplVulkan_AddTexture(sampler, image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
tex_data->DS = ImGui_ImplVulkan_AddTexture(tex_data->Sampler, tex_data->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
|
||||||
// Create Upload Buffer
|
// Create Upload Buffer
|
||||||
VkBuffer upload_buffer;
|
|
||||||
VkDeviceMemory upload_buffer_memory;
|
|
||||||
{
|
{
|
||||||
VkBufferCreateInfo buffer_info = {};
|
VkBufferCreateInfo buffer_info = {};
|
||||||
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
buffer_info.size = image_size;
|
buffer_info.size = image_size;
|
||||||
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &upload_buffer);
|
err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &tex_data->UploadBuffer);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
VkMemoryRequirements req;
|
VkMemoryRequirements req;
|
||||||
vkGetBufferMemoryRequirements(g_Device, upload_buffer, &req);
|
vkGetBufferMemoryRequirements(g_Device, tex_data->UploadBuffer, &req);
|
||||||
VkMemoryAllocateInfo alloc_info = {};
|
VkMemoryAllocateInfo alloc_info = {};
|
||||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
alloc_info.allocationSize = req.size;
|
alloc_info.allocationSize = req.size;
|
||||||
alloc_info.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
alloc_info.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
||||||
err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &upload_buffer_memory);
|
err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &tex_data->UploadBufferMemory);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
err = vkBindBufferMemory(g_Device, upload_buffer, upload_buffer_memory, 0);
|
err = vkBindBufferMemory(g_Device, tex_data->UploadBuffer, tex_data->UploadBufferMemory, 0);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload to Buffer:
|
// Upload to Buffer:
|
||||||
{
|
{
|
||||||
void* map = NULL;
|
void* map = NULL;
|
||||||
err = vkMapMemory(g_Device, upload_buffer_memory, 0, image_size, 0, &map);
|
err = vkMapMemory(g_Device, tex_data->UploadBufferMemory, 0, image_size, 0, &map);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
memcpy(map, image_data, image_size);
|
memcpy(map, image_data, image_size);
|
||||||
VkMappedMemoryRange range[1] = {};
|
VkMappedMemoryRange range[1] = {};
|
||||||
range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
||||||
range[0].memory = upload_buffer_memory;
|
range[0].memory = tex_data->UploadBufferMemory;
|
||||||
range[0].size = image_size;
|
range[0].size = image_size;
|
||||||
err = vkFlushMappedMemoryRanges(g_Device, 1, range);
|
err = vkFlushMappedMemoryRanges(g_Device, 1, range);
|
||||||
check_vk_result(err);
|
check_vk_result(err);
|
||||||
vkUnmapMemory(g_Device, upload_buffer_memory);
|
vkUnmapMemory(g_Device, tex_data->UploadBufferMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release image memory using stb
|
// Release image memory using stb
|
||||||
@ -724,7 +737,7 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
copy_barrier[0].image = texture_image;
|
copy_barrier[0].image = tex_data->Image;
|
||||||
copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
copy_barrier[0].subresourceRange.levelCount = 1;
|
copy_barrier[0].subresourceRange.levelCount = 1;
|
||||||
copy_barrier[0].subresourceRange.layerCount = 1;
|
copy_barrier[0].subresourceRange.layerCount = 1;
|
||||||
@ -733,10 +746,10 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
VkBufferImageCopy region = {};
|
VkBufferImageCopy region = {};
|
||||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
region.imageSubresource.layerCount = 1;
|
region.imageSubresource.layerCount = 1;
|
||||||
region.imageExtent.width = *image_width;
|
region.imageExtent.width = tex_data->Width;
|
||||||
region.imageExtent.height = *image_height;
|
region.imageExtent.height = tex_data->Height;
|
||||||
region.imageExtent.depth = 1;
|
region.imageExtent.depth = 1;
|
||||||
vkCmdCopyBufferToImage(command_buffer, upload_buffer, texture_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
vkCmdCopyBufferToImage(command_buffer, tex_data->UploadBuffer, tex_data->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||||
|
|
||||||
VkImageMemoryBarrier use_barrier[1] = {};
|
VkImageMemoryBarrier use_barrier[1] = {};
|
||||||
use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
@ -746,7 +759,7 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
use_barrier[0].image = texture_image;
|
use_barrier[0].image = tex_data->Image;
|
||||||
use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
use_barrier[0].subresourceRange.levelCount = 1;
|
use_barrier[0].subresourceRange.levelCount = 1;
|
||||||
use_barrier[0].subresourceRange.layerCount = 1;
|
use_barrier[0].subresourceRange.layerCount = 1;
|
||||||
@ -769,28 +782,45 @@ bool LoadTextureFromFile(const char* filename, VkDescriptorSet* img_ds, int* ima
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to cleanup an image loaded with LoadTextureFromFile
|
||||||
|
void RemoveTexture(MyTextureData* tex_data)
|
||||||
|
{
|
||||||
|
vkFreeMemory(g_Device, tex_data->UploadBufferMemory, nullptr);
|
||||||
|
vkDestroyBuffer(g_Device, tex_data->UploadBuffer, nullptr);
|
||||||
|
vkDestroySampler(g_Device, tex_data->Sampler, nullptr);
|
||||||
|
vkDestroyImageView(g_Device, tex_data->ImageView, nullptr);
|
||||||
|
vkDestroyImage(g_Device, tex_data->Image, nullptr);
|
||||||
|
vkFreeMemory(g_Device, tex_data->ImageMemory, nullptr);
|
||||||
|
ImGui_ImplVulkan_RemoveTexture(tex_data->DS);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Load our texture after initializing Vulkan loader (for example after `ImGui_ImplVulkan_Init()`):
|
Load our texture after initializing Vulkan loader (for example after `ImGui_ImplVulkan_Init()`):
|
||||||
```cpp
|
```cpp
|
||||||
int my_image_width = 0;
|
MyTextureData my_image_texture;
|
||||||
int my_image_height = 0;
|
bool ret = LoadTextureFromFile("../../MyImage01.jpg", &my_image_texture);
|
||||||
VkDescriptorSet my_image_texture = 0;
|
|
||||||
bool ret = LoadTextureFromFile("../../MyImage01.jpg", &my_image_texture, &my_image_width, &my_image_height);
|
|
||||||
IM_ASSERT(ret);
|
IM_ASSERT(ret);
|
||||||
```
|
```
|
||||||
|
|
||||||
In the snippet of code above, we added an assert `IM_ASSERT(ret)` to check if the image file was loaded correctly. You may also use your Debugger and confirm that `my_image_texture` is not zero and that `my_image_width` `my_image_width` are correct.
|
In the snippet of code above, we added an assert `IM_ASSERT(ret)` to check if the image file was loaded correctly. You may also use your Debugger and confirm that `my_image_texture` is not zero and that `my_image_texture.Width` `my_image_texture.Height` are correct.
|
||||||
|
|
||||||
Now that we have an Vulkan texture and its dimensions, we can display it in our main loop:
|
Now that we have an Vulkan texture and its dimensions, we can display it in our main loop:
|
||||||
```cpp
|
```cpp
|
||||||
ImGui::Begin("Vulkan Texture Text");
|
ImGui::Begin("Vulkan Texture Text");
|
||||||
ImGui::Text("pointer = %p", my_image_texture);
|
ImGui::Text("pointer = %p", my_image_texture.DS);
|
||||||
ImGui::Text("size = %d x %d", my_image_width, my_image_height);
|
ImGui::Text("size = %d x %d", my_image_texture.Width, my_image_texture.Height);
|
||||||
ImGui::Image((ImTextureID)my_image_texture, ImVec2(my_image_width, my_image_height));
|
ImGui::Image((ImTextureID)my_image_texture.DS, ImVec2(my_image_texture.Width, my_image_texture.Height));
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In the cleanup stage remember to call the RemoveTexture function to avoid leaks, and angry Vulkan Validation Layers! Call it before `ImGui_ImplVulkan_Shutdown()`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
RemoveTexture(&my_image_texture);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
// TODO replace with Vulkan image instead of this OpenGL placeholder
|
// TODO replace with Vulkan image instead of this OpenGL placeholder
|
||||||
|
|
||||||
![image](https://user-images.githubusercontent.com/8225057/65344387-dfc21180-dbd7-11e9-9478-627403721435.png)
|
![image](https://user-images.githubusercontent.com/8225057/65344387-dfc21180-dbd7-11e9-9478-627403721435.png)
|
||||||
|
Loading…
Reference in New Issue
Block a user