|
|
|
@@ -2,6 +2,8 @@
|
|
|
|
|
|
|
|
|
|
#include "wapp/wapp.h"
|
|
|
|
|
#include "vulkan_profiles/vulkan_profiles.h"
|
|
|
|
|
#include "ktx.h"
|
|
|
|
|
#include "ktxvulkan.h"
|
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
|
#include <glm/gtc/quaternion.hpp>
|
|
|
|
@@ -19,7 +21,9 @@
|
|
|
|
|
#include <vulkan/vk_enum_string_helper.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
// {{{ 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) {
|
|
|
|
|