void G_ImagesLoad(M_Arena *arena) { M_TempScope(1, &arena) { FS_List assets = FS_PathList(temp.arena, S("assets")); Vk_Buffer staging = Vk_BufferCreate(MB(256), true /* host_visible */); U8 *base = staging.data; U64 offset = 0; Vk_CommandBuffer *cmds = Vk_CommandBufferPush(); U32 n_images = 0; for (FS_Entry *it = assets.first; it != 0; it = it->next) { if (Str8_EndsWith(it->basename, S("png"))) { n_images += 1; } } VkBufferImageCopy copy = { 0 }; G_Image *images = M_ArenaPush(arena, G_Image, .count = n_images); n_images = 0; // Image upload is sbi_load -> copy to staging -> upload to gpu texture for (FS_Entry *it = assets.first; it != 0; it = it->next) { if (Str8_EndsWith(it->basename, S("png"))) { S32 w, h, c; stbi_uc *data = stbi_load((const char *) it->path.data, &w, &h, &c, 4); if (data) { G_Image *image = &images[n_images]; U64 image_sz = 4 * w * h; M_CopySize(base, data, image_sz); copy.bufferOffset = offset; copy.bufferRowLength = 0; copy.bufferImageHeight = 0; copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy.imageSubresource.mipLevel = 0; copy.imageSubresource.baseArrayLayer = 0; copy.imageSubresource.layerCount = 1; copy.imageExtent.width = w; copy.imageExtent.height = h; copy.imageExtent.depth = 1; base += image_sz; offset += image_sz; Assert(offset <= staging.size); n_images += 1; image->name = Str8_Copy(arena, Str8_RemoveAfterLast(it->basename, '.')); image->image.width = w; image->image.height = h; image->image.format = VK_FORMAT_R8G8B8A8_SRGB; image->image.usage = VK_IMAGE_USAGE_SAMPLED_BIT; Vk_ImageCreate(&image->image); VkImageMemoryBarrier2 transfer = { 0 }; VkImageMemoryBarrier2 shader_read = { 0 }; transfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; transfer.srcStageMask = VK_PIPELINE_STAGE_2_NONE; transfer.srcAccessMask = VK_ACCESS_2_NONE; transfer.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; transfer.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; transfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; transfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; transfer.image = image->image.handle; transfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; transfer.subresourceRange.layerCount = 1; transfer.subresourceRange.levelCount = 1; shader_read.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; shader_read.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; shader_read.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; shader_read.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; shader_read.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; shader_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; shader_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; shader_read.image = image->image.handle; shader_read.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; shader_read.subresourceRange.layerCount = 1; shader_read.subresourceRange.levelCount = 1; VkDependencyInfo dep = { 0 }; dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dep.imageMemoryBarrierCount = 1; dep.pImageMemoryBarriers = &transfer; vk.CmdPipelineBarrier2(cmds->handle, &dep); vk.CmdCopyBufferToImage(cmds->handle, staging.handle, image->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); dep.pImageMemoryBarriers = &shader_read; vk.CmdPipelineBarrier2(cmds->handle, &dep); stbi_image_free(data); } } } Vk_CommandBufferSubmit(cmds, true /* wait */); } }