From 49b9f5778a09e3e5f7e215481e696740c33b0abd Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sat, 20 Jun 2026 17:50:14 +0100 Subject: [PATCH] Load textures --- main.cpp | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/main.cpp b/main.cpp index fdcb02f..ddbff24 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,8 @@ #include "wapp/wapp.h" #include "vulkan_profiles/vulkan_profiles.h" +#include "ktx.h" +#include "ktxvulkan.h" #include #include #include @@ -19,7 +21,9 @@ #include #include #include +#include +// {{{ Exit Codes enum ExitCode { EXIT_CODE_SUCCESS, EXIT_CODE_SDL_INIT_FAILED, @@ -39,7 +43,9 @@ enum ExitCode { EXIT_CODE_MESH_LOAD_FAILED, EXIT_CODE_SYNC_OBJ_CREATE_FAILED, }; +// }}} +// {{{ Types struct Vertex { glm::vec3 pos; glm::vec3 normal; @@ -61,6 +67,13 @@ struct ShaderDataBuffer { VkDeviceAddress device_address; }; +struct Texture { + VmaAllocation allocation; + VkImage image; + VkImageView view; + VkSampler sampler; +}; + typedef VkPhysicalDevice *VkPhysicalDeviceArray; typedef VkQueueFamilyProperties2 *VkQueueFamilyProperties2Array; typedef VkImage *VkImageArray; @@ -71,12 +84,20 @@ typedef ShaderDataBuffer *ShaderDataBufferArray; typedef VkFence *VkFenceArray; typedef VkSemaphore *VkSemaphoreArray; typedef VkCommandBuffer *VkCommandBufferArray; +typedef Texture *TextureArray; +typedef VkDescriptorImageInfo *VkDescriptorImageInfoArray; +typedef VkBufferImageCopy *VkBufferImageCopyArray; +// }}} +// {{{ Helper Function Declarations wapp_intern inline void check(VkResult result); wapp_intern inline void check_swapchain(VkResult result); wapp_intern inline void check(bool result, i32 code); +// }}} +// {{{ Global Variables wapp_intern constexpr u32 max_frames_in_flight = 2; +wapp_intern constexpr u32 texture_count = 3; wapp_intern bool running = true; wapp_intern bool update_swapchain = false; wapp_intern f32 display_scale = 1.0f; @@ -112,6 +133,9 @@ wapp_intern VkSemaphoreArray image_acquired_semaphores; wapp_intern VkSemaphoreArray render_completed_semaphores = nullptr; wapp_intern VkCommandPool command_pool = VK_NULL_HANDLE; wapp_intern VkCommandBufferArray command_buffers; +wapp_intern TextureArray textures; +wapp_intern VkDescriptorImageInfoArray tex_descriptors; +// }}} int main() { // {{{ Initialisation @@ -589,7 +613,202 @@ int main() { // }}} // Texture Images {{{ + textures = wapp_array_with_capacity(Texture, texture_count, ARRAY_INIT_FILLED); + tex_descriptors = wapp_array_with_capacity(VkDescriptorImageInfo, texture_count, ARRAY_INIT_FILLED); + constexpr u32 buf_size = 2048; + for (u32 i = 0; i < wapp_array_count(textures); ++i) { + // {{{ Load Textures From File + ktxTexture *texture = nullptr; + char buf[buf_size] = {}; + Str8 filename = wapp_str8_buf(buf_size); + wapp_str8_format(&filename, "assets/suzanne%i.ktx", i); + wapp_str8_copy_to_cstr(buf, &filename, buf_size); + ktxTexture_CreateFromNamedFile(buf, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture); + // }}} + + // {{{ Create Image And View + VkImageCreateInfo tex_create_info = {}; + tex_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + tex_create_info.imageType = VK_IMAGE_TYPE_2D; + tex_create_info.format = ktxTexture_GetVkFormat(texture); + tex_create_info.extent.width = texture->baseWidth; + tex_create_info.extent.height = texture->baseHeight; + tex_create_info.extent.depth = 1; + tex_create_info.mipLevels = texture->numLevels; + tex_create_info.arrayLayers = 1; + tex_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + tex_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + tex_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + tex_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + tex_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VmaAllocationCreateInfo tex_alloc_info = {}; + tex_alloc_info.usage = VMA_MEMORY_USAGE_AUTO; + + check(vmaCreateImage(allocator, &tex_create_info, &tex_alloc_info, &textures[i].image, + &textures[i].allocation, nullptr)); + + VkImageViewCreateInfo tex_view_create_info = {}; + tex_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + tex_view_create_info.image = textures[i].image; + tex_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + tex_view_create_info.format = tex_create_info.format; + tex_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + tex_view_create_info.subresourceRange.levelCount = tex_create_info.mipLevels; + tex_view_create_info.subresourceRange.layerCount = 1; + + check(vkCreateImageView(device, &tex_view_create_info, nullptr, &textures[i].view)); + // }}} + + // {{{ Create Intermediate Buffer And Upload Texture Data + VkBuffer img_src_buf = {}; + VmaAllocation img_src_allocation = {}; + VmaAllocationInfo img_src_alloc_info = {}; + + VkBufferCreateInfo img_src_buf_create_info = {}; + img_src_buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + img_src_buf_create_info.size = (u32)texture->dataSize; + img_src_buf_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + img_src_buf_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo img_src_buf_alloc_create_info = {}; + img_src_buf_alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + img_src_buf_alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO; + + check(vmaCreateBuffer(allocator, &img_src_buf_create_info, &img_src_buf_alloc_create_info, + &img_src_buf, &img_src_allocation, &img_src_alloc_info)); + memcpy(img_src_alloc_info.pMappedData, texture->pData, texture->dataSize); + // }}} + + // {{{ Create Fence For Synchronization + VkFence fence_one_time = {}; + VkFenceCreateInfo fence_one_time_create_info = {}; + fence_one_time_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + check(vkCreateFence(device, &fence_one_time_create_info, NULL, &fence_one_time)); + // }}} + + // {{{ Allocate Temp Command Buffer + VkCommandBuffer cb_one_time = {}; + VkCommandBufferAllocateInfo cb_one_time_alloc_info = {}; + cb_one_time_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cb_one_time_alloc_info.commandPool = command_pool; + cb_one_time_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cb_one_time_alloc_info.commandBufferCount = 1; + check(vkAllocateCommandBuffers(device, &cb_one_time_alloc_info, &cb_one_time)); + // }}} + + wapp_mem_arena_allocator_temp_begin(&arena); + + // {{{ Record Commands To Copy Texture Data To Image + VkCommandBufferBeginInfo cb_one_time_begin_info = {}; + cb_one_time_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cb_one_time_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + check(vkBeginCommandBuffer(cb_one_time, &cb_one_time_begin_info)); + + // {{{ Transition Image Layout For Data Transfer + VkImageMemoryBarrier2 barrier_tex_image = {}; + barrier_tex_image.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier_tex_image.srcStageMask = VK_PIPELINE_STAGE_2_NONE; + barrier_tex_image.srcAccessMask = VK_ACCESS_2_NONE; + barrier_tex_image.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + barrier_tex_image.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + barrier_tex_image.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier_tex_image.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier_tex_image.image = textures[i].image; + barrier_tex_image.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier_tex_image.subresourceRange.levelCount = texture->numLevels; + barrier_tex_image.subresourceRange.layerCount = 1; + + VkDependencyInfo barrier_tex_info = {}; + barrier_tex_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + barrier_tex_info.imageMemoryBarrierCount = 1; + barrier_tex_info.pImageMemoryBarriers = &barrier_tex_image; + + vkCmdPipelineBarrier2(cb_one_time, &barrier_tex_info); + // }}} + + // {{{ Copy All Mip Levels + VkBufferImageCopyArray copy_regions = wapp_array_alloc_capacity(VkBufferImageCopy, &arena, + texture->numLevels, ARRAY_INIT_NONE); + for (u32 j = 0; j < texture->numLevels; ++j) { + ktx_size_t mip_offset = 0; + KTX_error_code ret = ktxTexture_GetImageOffset(texture, j, 0, 0, &mip_offset); + + VkBufferImageCopy buf_img_copy = {}; + buf_img_copy.bufferOffset = mip_offset; + buf_img_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + buf_img_copy.imageSubresource.mipLevel = j; + buf_img_copy.imageSubresource.layerCount = 1; + buf_img_copy.imageExtent.width = texture->baseWidth >> j; + buf_img_copy.imageExtent.height = texture->baseHeight >> j; + buf_img_copy.imageExtent.depth = 1; + + wapp_array_append_capped(VkBufferImageCopy, copy_regions, &buf_img_copy); + } + + vkCmdCopyBufferToImage(cb_one_time, img_src_buf, textures[i].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + wapp_array_count(copy_regions), copy_regions); + // }}} + + // {{{ Transition Image Layout For Reading + VkImageMemoryBarrier2 barrier_tex_read = {}; + barrier_tex_read.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier_tex_read.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + barrier_tex_read.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + barrier_tex_read.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + barrier_tex_read.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; + barrier_tex_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier_tex_read.newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL; + barrier_tex_read.image = textures[i].image; + barrier_tex_read.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier_tex_read.subresourceRange.levelCount = texture->numLevels; + barrier_tex_read.subresourceRange.layerCount = 1; + + barrier_tex_info.pImageMemoryBarriers = &barrier_tex_read; + + vkCmdPipelineBarrier2(cb_one_time, &barrier_tex_info); + // }}} + + check(vkEndCommandBuffer(cb_one_time)); + // }}} + + // {{{ Submit Command Buffer + VkSubmitInfo cb_submit_info = {}; + cb_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + cb_submit_info.commandBufferCount = 1; + cb_submit_info.pCommandBuffers = &cb_one_time; + + check(vkQueueSubmit(queue, 1, &cb_submit_info, fence_one_time)); + check(vkWaitForFences(device, 1, &fence_one_time, VK_TRUE, UINT64_MAX)); + // }}} + + wapp_mem_arena_allocator_temp_end(&arena); + + // {{{ Create Sampler And Setup Descriptor + VkSamplerCreateInfo sampler_create_info = {}; + sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_create_info.magFilter = VK_FILTER_LINEAR; + sampler_create_info.minFilter = VK_FILTER_LINEAR; + sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_create_info.anisotropyEnable = VK_TRUE; + sampler_create_info.maxAnisotropy = 8.0f; + sampler_create_info.maxLod = VK_LOD_CLAMP_NONE; + + check(vkCreateSampler(device, &sampler_create_info, NULL, &textures[i].sampler)); + + tex_descriptors[i].imageView = textures[i].view; + tex_descriptors[i].sampler = textures[i].sampler; + tex_descriptors[i].imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL; + // }}} + + // {{{ Clean Temp Objects + vkDestroyFence(device, fence_one_time, NULL); + vmaDestroyBuffer(allocator, img_src_buf, img_src_allocation); + ktxTexture_Destroy(texture); + // }}} + } // }}} // {{{ Render Loop @@ -611,6 +830,11 @@ int main() { // }}} // {{{ Cleanup + for (u32 i = 0; i < wapp_array_count(textures); ++i) { + vkDestroySampler(device, textures[i].sampler, NULL); + vkDestroyImageView(device, textures[i].view, NULL); + vmaDestroyImage(allocator, textures[i].image, textures[i].allocation); + } vkFreeCommandBuffers(device, command_pool, max_frames_in_flight, command_buffers); vkDestroyCommandPool(device, command_pool, NULL); for (u32 i = 0; i < swapchain_image_count; ++i) {