Amend Vulkan code for formatting

omar 2022-11-15 11:10:00 +01:00
parent 504473bcaf
commit 0423015a24

@ -573,217 +573,211 @@ struct MyTextureData
// 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)
{ {
VkPhysicalDeviceMemoryProperties mem_properties; VkPhysicalDeviceMemoryProperties mem_properties;
vkGetPhysicalDeviceMemoryProperties(g_PhysicalDevice, &mem_properties); vkGetPhysicalDeviceMemoryProperties(g_PhysicalDevice, &mem_properties);
for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++)
{ if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties)
if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) return i;
{
return i;
}
}
return 0xFFFFFFFF; // Unable to find memoryType return 0xFFFFFFFF; // Unable to find memoryType
} }
// Helper function to load an image with common settings and return a MyTextureData with 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, MyTextureData* tex_data) 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
tex_data->Channels = 4; tex_data->Channels = 4;
unsigned char* image_data = stbi_load(filename, &tex_data->Width, &tex_data->Height, 0, tex_data->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)
{ return false;
return false;
}
// Calculate allocation size (in number of bytes) // Calculate allocation size (in number of bytes)
size_t image_size = tex_data->Width*tex_data->Height*tex_data->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.
{ {
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 = tex_data->Width; info.extent.width = tex_data->Width;
info.extent.height = tex_data->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;
info.samples = VK_SAMPLE_COUNT_1_BIT; info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL; info.tiling = VK_IMAGE_TILING_OPTIMAL;
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, &tex_data->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, tex_data->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, &tex_data->ImageMemory); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &tex_data->ImageMemory);
check_vk_result(err); check_vk_result(err);
err = vkBindImageMemory(g_Device, tex_data->Image, tex_data->ImageMemory, 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
{ {
VkImageViewCreateInfo info = {}; VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = tex_data->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, &tex_data->ImageView); err = vkCreateImageView(g_Device, &info, g_Allocator, &tex_data->ImageView);
check_vk_result(err); check_vk_result(err);
} }
// Create Sampler // Create 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;
sampler_info.magFilter = VK_FILTER_LINEAR; sampler_info.magFilter = VK_FILTER_LINEAR;
sampler_info.minFilter = VK_FILTER_LINEAR; sampler_info.minFilter = VK_FILTER_LINEAR;
sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // outside image bounds just use border color sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // outside image bounds just use border color
sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
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, &tex_data->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
tex_data->DS = ImGui_ImplVulkan_AddTexture(tex_data->Sampler, tex_data->ImageView, 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
{ {
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, &tex_data->UploadBuffer); 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, tex_data->UploadBuffer, &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, &tex_data->UploadBufferMemory); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &tex_data->UploadBufferMemory);
check_vk_result(err); check_vk_result(err);
err = vkBindBufferMemory(g_Device, tex_data->UploadBuffer, tex_data->UploadBufferMemory, 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, tex_data->UploadBufferMemory, 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 = tex_data->UploadBufferMemory; 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, tex_data->UploadBufferMemory); vkUnmapMemory(g_Device, tex_data->UploadBufferMemory);
} }
// Release image memory using stb // Release image memory using stb
stbi_image_free(image_data); stbi_image_free(image_data);
// Create a command buffer that will perform following steps when hit in the command queue. // Create a command buffer that will perform following steps when hit in the command queue.
// TODO: this works in the example, but may need input if this is an acceptable way to access the pool/create the command buffer. // TODO: this works in the example, but may need input if this is an acceptable way to access the pool/create the command buffer.
VkCommandPool command_pool = g_MainWindowData.Frames[g_MainWindowData.FrameIndex].CommandPool; VkCommandPool command_pool = g_MainWindowData.Frames[g_MainWindowData.FrameIndex].CommandPool;
VkCommandBuffer command_buffer; VkCommandBuffer command_buffer;
{ {
VkCommandBufferAllocateInfo alloc_info{}; VkCommandBufferAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandPool = command_pool; alloc_info.commandPool = command_pool;
alloc_info.commandBufferCount = 1; alloc_info.commandBufferCount = 1;
err = vkAllocateCommandBuffers(g_Device, &alloc_info, &command_buffer); err = vkAllocateCommandBuffers(g_Device, &alloc_info, &command_buffer);
check_vk_result(err); check_vk_result(err);
VkCommandBufferBeginInfo begin_info = {}; VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(command_buffer, &begin_info); err = vkBeginCommandBuffer(command_buffer, &begin_info);
check_vk_result(err); check_vk_result(err);
} }
// Copy to Image // Copy to Image
{ {
VkImageMemoryBarrier copy_barrier[1] = {}; VkImageMemoryBarrier copy_barrier[1] = {};
copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
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 = tex_data->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;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier); vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier);
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 = tex_data->Width; region.imageExtent.width = tex_data->Width;
region.imageExtent.height = tex_data->Height; region.imageExtent.height = tex_data->Height;
region.imageExtent.depth = 1; region.imageExtent.depth = 1;
vkCmdCopyBufferToImage(command_buffer, tex_data->UploadBuffer, tex_data->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region); vkCmdCopyBufferToImage(command_buffer, tex_data->UploadBuffer, tex_data->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
VkImageMemoryBarrier use_barrier[1] = {};
use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
use_barrier[0].image = tex_data->Image;
use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
use_barrier[0].subresourceRange.levelCount = 1;
use_barrier[0].subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier);
}
// End command buffer VkImageMemoryBarrier use_barrier[1] = {};
{ use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
VkSubmitInfo end_info = {}; use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
end_info.commandBufferCount = 1; use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
end_info.pCommandBuffers = &command_buffer; use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
err = vkEndCommandBuffer(command_buffer); use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
check_vk_result(err); use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE); use_barrier[0].image = tex_data->Image;
check_vk_result(err); use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
err = vkDeviceWaitIdle(g_Device); use_barrier[0].subresourceRange.levelCount = 1;
check_vk_result(err); use_barrier[0].subresourceRange.layerCount = 1;
} vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier);
}
return true; // End command buffer
{
VkSubmitInfo end_info = {};
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
end_info.commandBufferCount = 1;
end_info.pCommandBuffers = &command_buffer;
err = vkEndCommandBuffer(command_buffer);
check_vk_result(err);
err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE);
check_vk_result(err);
err = vkDeviceWaitIdle(g_Device);
check_vk_result(err);
}
return true;
} }
// Helper function to cleanup an image loaded with LoadTextureFromFile // Helper function to cleanup an image loaded with LoadTextureFromFile
void RemoveTexture(MyTextureData* tex_data) void RemoveTexture(MyTextureData* tex_data)
{ {
vkFreeMemory(g_Device, tex_data->UploadBufferMemory, nullptr); vkFreeMemory(g_Device, tex_data->UploadBufferMemory, nullptr);
@ -798,8 +792,8 @@ void RemoveTexture(MyTextureData* tex_data)
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
MyTextureData my_image_texture; MyTextureData my_texture;
bool ret = LoadTextureFromFile("../../MyImage01.jpg", &my_image_texture); bool ret = LoadTextureFromFile("../../MyImage01.jpg", &my_texture);
IM_ASSERT(ret); IM_ASSERT(ret);
``` ```
@ -808,16 +802,16 @@ In the snippet of code above, we added an assert `IM_ASSERT(ret)` to check if th
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.DS); ImGui::Text("pointer = %p", my_texture.DS);
ImGui::Text("size = %d x %d", my_image_texture.Width, my_image_texture.Height); ImGui::Text("size = %d x %d", my_texture.Width, my_texture.Height);
ImGui::Image((ImTextureID)my_image_texture.DS, ImVec2(my_image_texture.Width, my_image_texture.Height)); ImGui::Image((ImTextureID)my_texture.DS, ImVec2(my_texture.Width, my_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()` 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 ```cpp
RemoveTexture(&my_image_texture); RemoveTexture(&my_texture);
``` ```