|
|
|
@@ -1,9 +1,12 @@
|
|
|
|
|
// vim:fileencoding=utf-8:foldmethod=marker
|
|
|
|
|
|
|
|
|
|
#include "wapp/wapp.h"
|
|
|
|
|
#include "vulkan/vulkan_core.h"
|
|
|
|
|
#include "vulkan_profiles/vulkan_profiles.h"
|
|
|
|
|
#include "ktx.h"
|
|
|
|
|
#include "ktxvulkan.h"
|
|
|
|
|
#include <SDL3/SDL_timer.h>
|
|
|
|
|
#include <glm/ext/matrix_clip_space.hpp>
|
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
|
#include <glm/gtc/quaternion.hpp>
|
|
|
|
@@ -13,6 +16,8 @@
|
|
|
|
|
#include <SDL3/SDL_keycode.h>
|
|
|
|
|
#include <SDL3/SDL_video.h>
|
|
|
|
|
#include <SDL3/SDL_vulkan.h>
|
|
|
|
|
#include <slang/slang.h>
|
|
|
|
|
#include <slang/slang-com-ptr.h>
|
|
|
|
|
#include <tiny_obj_loader.h>
|
|
|
|
|
#define VMA_IMPLEMENTATION
|
|
|
|
|
#include <vma/vk_mem_alloc.h>
|
|
|
|
@@ -87,6 +92,13 @@ typedef VkCommandBuffer *VkCommandBufferArray;
|
|
|
|
|
typedef Texture *TextureArray;
|
|
|
|
|
typedef VkDescriptorImageInfo *VkDescriptorImageInfoArray;
|
|
|
|
|
typedef VkBufferImageCopy *VkBufferImageCopyArray;
|
|
|
|
|
typedef slang::TargetDesc *SlangTargetDescArray;
|
|
|
|
|
typedef slang::CompilerOptionEntry *SlangCompOptEntryArray;
|
|
|
|
|
typedef VkVertexInputAttributeDescription *VkVertexInputAttributeDescriptionArray;
|
|
|
|
|
typedef VkPipelineShaderStageCreateInfo *VkPipelineShaderStageCreateInfoArray;
|
|
|
|
|
typedef VkDynamicState *VkDynamicStateArray;
|
|
|
|
|
typedef glm::vec3 *GlmVec3Array;
|
|
|
|
|
typedef VkImageMemoryBarrier2 *VkImageMemoryBarrier2Array;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Helper Function Declarations
|
|
|
|
@@ -96,7 +108,10 @@ wapp_intern inline void check(bool result, i32 code);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Global Variables
|
|
|
|
|
wapp_intern u32 frame_index = 0;
|
|
|
|
|
wapp_intern u32 image_index = 0;
|
|
|
|
|
wapp_intern constexpr u32 max_frames_in_flight = 2;
|
|
|
|
|
wapp_intern constexpr u32 instance_count = 3;
|
|
|
|
|
wapp_intern constexpr u32 texture_count = 3;
|
|
|
|
|
wapp_intern bool running = true;
|
|
|
|
|
wapp_intern bool update_swapchain = false;
|
|
|
|
@@ -129,12 +144,21 @@ wapp_intern VmaAllocation vert_index_buf_alloc = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern ShaderData shader_data = {};
|
|
|
|
|
wapp_intern ShaderDataBufferArray shader_data_bufs;
|
|
|
|
|
wapp_intern VkFenceArray fences;
|
|
|
|
|
wapp_intern VkSemaphoreArray image_acquired_semaphores;
|
|
|
|
|
wapp_intern VkSemaphoreArray image_acquired_semaphores = nullptr;
|
|
|
|
|
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;
|
|
|
|
|
wapp_intern VkDescriptorSetLayout desc_set_layout_tex = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern VkDescriptorPool desc_pool = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern VkDescriptorSet desc_set_tex = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern Slang::ComPtr<slang::IGlobalSession> slang_global_session;
|
|
|
|
|
wapp_intern VkShaderModule shader_module = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern VkPipelineLayout graphics_pipeline_layout = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern VkPipeline graphics_pipeline = VK_NULL_HANDLE;
|
|
|
|
|
wapp_intern glm::vec3 camera_position = {0.0f, 0.0f, -6.0f};
|
|
|
|
|
wapp_intern GlmVec3Array object_rotations;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
@@ -146,8 +170,8 @@ int main() {
|
|
|
|
|
check(volkInitialize());
|
|
|
|
|
|
|
|
|
|
display_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
|
|
|
|
|
window = SDL_CreateWindow("How To Vulkan", (i32)(display_scale * 1920),
|
|
|
|
|
(i32)(display_scale * 1080), SDL_WINDOW_VULKAN);
|
|
|
|
|
window = SDL_CreateWindow("How To Vulkan", (i32)(display_scale * 1920), (i32)(display_scale * 1080),
|
|
|
|
|
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
|
|
|
|
|
check(window != nullptr, EXIT_CODE_WINDOW_CREATION_FAILED);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
@@ -350,7 +374,8 @@ int main() {
|
|
|
|
|
|
|
|
|
|
VmaAllocatorCreateInfo allocator_create_info = {};
|
|
|
|
|
allocator_create_info.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT |
|
|
|
|
|
VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT;
|
|
|
|
|
VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT |
|
|
|
|
|
VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT;
|
|
|
|
|
allocator_create_info.instance = instance;
|
|
|
|
|
allocator_create_info.physicalDevice = physical_device;
|
|
|
|
|
allocator_create_info.device = device;
|
|
|
|
@@ -505,15 +530,15 @@ int main() {
|
|
|
|
|
1.0 - attrib.texcoords[index.texcoord_index * 2 + 1]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
u16 idx = (u16)wapp_array_count(indices);
|
|
|
|
|
wapp_array_append_alloc(Vertex, &arena, vertices, &v, ARRAY_INIT_NONE);
|
|
|
|
|
wapp_array_append_alloc(u16, &arena, indices, &idx, ARRAY_INIT_NONE);
|
|
|
|
|
u16 idx = (u16)wapp_array_count(indices);
|
|
|
|
|
vertices = wapp_array_append_alloc(Vertex, &arena, vertices, &v, ARRAY_INIT_NONE);
|
|
|
|
|
indices = wapp_array_append_alloc(u16, &arena, indices, &idx, ARRAY_INIT_NONE);
|
|
|
|
|
}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Upload to GPU
|
|
|
|
|
VkDeviceSize vertex_buf_size = sizeof(Vertex) * wapp_array_count(vertices);
|
|
|
|
|
VkDeviceSize index_buf_size = sizeof(u16) * wapp_array_count(vertices);
|
|
|
|
|
VkDeviceSize index_buf_size = sizeof(u16) * wapp_array_count(indices);
|
|
|
|
|
|
|
|
|
|
VkBufferCreateInfo buf_create_info = {};
|
|
|
|
|
buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
|
|
@@ -541,20 +566,20 @@ int main() {
|
|
|
|
|
shader_data_bufs = wapp_array_with_capacity(ShaderDataBuffer, max_frames_in_flight, ARRAY_INIT_FILLED);
|
|
|
|
|
for (u32 i = 0; i < max_frames_in_flight; ++i) {
|
|
|
|
|
// {{{ Create Buffer
|
|
|
|
|
VkBufferUsageFlags2CreateInfo data_buf_usage_flags = {};
|
|
|
|
|
data_buf_usage_flags.sType = VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO;
|
|
|
|
|
data_buf_usage_flags.usage = VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
|
|
|
|
|
VkBufferUsageFlags2CreateInfo buf_usage_flags = {};
|
|
|
|
|
buf_usage_flags.sType = VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO;
|
|
|
|
|
buf_usage_flags.usage = VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
|
|
|
|
|
|
|
|
|
|
VkBufferCreateInfo data_buf_create_info = {};
|
|
|
|
|
data_buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
|
|
|
data_buf_create_info.size = sizeof(ShaderData);
|
|
|
|
|
data_buf_create_info.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
|
|
|
data_buf_create_info.pNext = &data_buf_usage_flags;
|
|
|
|
|
data_buf_create_info.pNext = &buf_usage_flags;
|
|
|
|
|
|
|
|
|
|
VmaAllocationCreateInfo data_buf_alloc_create_info = {};
|
|
|
|
|
data_buf_alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
|
|
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
|
|
|
|
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
|
|
|
data_buf_create_info.usage = VMA_MEMORY_USAGE_AUTO;
|
|
|
|
|
data_buf_alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO;
|
|
|
|
|
|
|
|
|
|
check(vmaCreateBuffer(allocator, &data_buf_create_info, &data_buf_alloc_create_info,
|
|
|
|
|
&shader_data_bufs[i].buffer, &shader_data_bufs[i].allocation,
|
|
|
|
@@ -612,6 +637,8 @@ int main() {
|
|
|
|
|
check(vkAllocateCommandBuffers(device, &buffer_alloc_info, command_buffers));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
wapp_mem_arena_allocator_temp_begin(&arena);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
@@ -699,8 +726,6 @@ int main() {
|
|
|
|
|
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;
|
|
|
|
@@ -784,8 +809,6 @@ int main() {
|
|
|
|
|
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;
|
|
|
|
@@ -809,11 +832,511 @@ int main() {
|
|
|
|
|
ktxTexture_Destroy(texture);
|
|
|
|
|
// }}}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// {{{ Descriptor Indexing
|
|
|
|
|
// A descriptor is a handle that describes a shader resource.
|
|
|
|
|
//
|
|
|
|
|
// Descriptors are added into Descriptor Sets which are allocated from a Descriptor Pool.
|
|
|
|
|
// A descriptor set object is an opaque object containing storage for a set of descriptors,
|
|
|
|
|
// where the types and number of descriptors is defined by a descriptor set layout.
|
|
|
|
|
//
|
|
|
|
|
// Each descriptor set has a layout. A descriptor set layout object is defined by an array of
|
|
|
|
|
// zero or more descriptor bindings. Each individual descriptor binding is specified by
|
|
|
|
|
// a descriptor type, a count (array size) of the number of descriptors in the binding, a set
|
|
|
|
|
// of shader stages that can access the binding, and (if using immutable samplers) an array of
|
|
|
|
|
// sampler descriptors.
|
|
|
|
|
|
|
|
|
|
// {{{ Create Descriptor Set Layout
|
|
|
|
|
// Create a single binding with variable number of descriptors
|
|
|
|
|
u32 texture_count = (u32)wapp_array_count(textures);
|
|
|
|
|
|
|
|
|
|
VkDescriptorBindingFlags desc_variable_flag = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT;
|
|
|
|
|
|
|
|
|
|
VkDescriptorSetLayoutBindingFlagsCreateInfo desc_binding_flags = {};
|
|
|
|
|
desc_binding_flags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
|
|
|
|
|
desc_binding_flags.bindingCount = 1;
|
|
|
|
|
desc_binding_flags.pBindingFlags = &desc_variable_flag;
|
|
|
|
|
|
|
|
|
|
VkDescriptorSetLayoutBinding desc_layout_binding_tex = {};
|
|
|
|
|
// We combine texture images and samplers, so the binding's type needs to be
|
|
|
|
|
// VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
|
|
|
|
|
desc_layout_binding_tex.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
|
|
|
desc_layout_binding_tex.descriptorCount = texture_count;
|
|
|
|
|
// We only need to access this from the fragment shader, so we set stageFlags to
|
|
|
|
|
// VK_SHADER_STAGE_FRAGMENT_BIT
|
|
|
|
|
desc_layout_binding_tex.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
|
|
|
|
|
|
VkDescriptorSetLayoutCreateInfo desc_set_layout_create_info = {};
|
|
|
|
|
desc_set_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
|
|
|
desc_set_layout_create_info.pNext = &desc_binding_flags;
|
|
|
|
|
desc_set_layout_create_info.bindingCount = 1;
|
|
|
|
|
desc_set_layout_create_info.pBindings = &desc_layout_binding_tex;
|
|
|
|
|
|
|
|
|
|
check(vkCreateDescriptorSetLayout(device, &desc_set_layout_create_info, NULL, &desc_set_layout_tex));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Create Descriptor Pool To Allocate Descriptors
|
|
|
|
|
VkDescriptorPoolSize desc_pool_size = {};
|
|
|
|
|
desc_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
|
|
|
desc_pool_size.descriptorCount = texture_count;
|
|
|
|
|
|
|
|
|
|
VkDescriptorPoolCreateInfo desc_pool_create_info = {};
|
|
|
|
|
desc_pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
|
|
|
desc_pool_create_info.maxSets = 1;
|
|
|
|
|
desc_pool_create_info.poolSizeCount = 1;
|
|
|
|
|
desc_pool_create_info.pPoolSizes = &desc_pool_size;
|
|
|
|
|
|
|
|
|
|
check(vkCreateDescriptorPool(device, &desc_pool_create_info, NULL, &desc_pool));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Allocate Descriptor Sets
|
|
|
|
|
// Descriptor indexing allocate info
|
|
|
|
|
VkDescriptorSetVariableDescriptorCountAllocateInfo var_desc_count_alloc_info = {};
|
|
|
|
|
var_desc_count_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO;
|
|
|
|
|
var_desc_count_alloc_info.descriptorSetCount = 1;
|
|
|
|
|
var_desc_count_alloc_info.pDescriptorCounts = &texture_count;
|
|
|
|
|
|
|
|
|
|
VkDescriptorSetAllocateInfo tex_desc_set_alloc_info = {};
|
|
|
|
|
tex_desc_set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
|
|
|
tex_desc_set_alloc_info.pNext = &var_desc_count_alloc_info;
|
|
|
|
|
tex_desc_set_alloc_info.descriptorPool = desc_pool;
|
|
|
|
|
tex_desc_set_alloc_info.descriptorSetCount = 1;
|
|
|
|
|
tex_desc_set_alloc_info.pSetLayouts = &desc_set_layout_tex;
|
|
|
|
|
|
|
|
|
|
check(vkAllocateDescriptorSets(device, &tex_desc_set_alloc_info, &desc_set_tex));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Write Descriptor Set Data
|
|
|
|
|
VkWriteDescriptorSet write_desc_set_tex = {};
|
|
|
|
|
write_desc_set_tex.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
|
|
|
write_desc_set_tex.dstSet = desc_set_tex;
|
|
|
|
|
write_desc_set_tex.dstBinding = 0;
|
|
|
|
|
write_desc_set_tex.descriptorCount = (u32)wapp_array_count(tex_descriptors);
|
|
|
|
|
write_desc_set_tex.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
|
|
|
write_desc_set_tex.pImageInfo = tex_descriptors;
|
|
|
|
|
|
|
|
|
|
vkUpdateDescriptorSets(device, 1, &write_desc_set_tex, 0, NULL);
|
|
|
|
|
// }}}
|
|
|
|
|
// }}}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
wapp_mem_arena_allocator_temp_end(&arena);
|
|
|
|
|
|
|
|
|
|
// {{{ Shaders
|
|
|
|
|
// {{{ Runtime Compile Shaders
|
|
|
|
|
// {{{ Initialise Slang Session
|
|
|
|
|
// Global session: the connection between the application and the Slang library
|
|
|
|
|
slang::createGlobalSession(slang_global_session.writeRef());
|
|
|
|
|
|
|
|
|
|
// {{{ Define Compilation Scope
|
|
|
|
|
slang::TargetDesc target = {};
|
|
|
|
|
target.format = SLANG_SPIRV;
|
|
|
|
|
target.profile = {slang_global_session->findProfile("spirv_1_4")};
|
|
|
|
|
|
|
|
|
|
SlangTargetDescArray slang_targets = wapp_array_with_capacity(slang::TargetDesc, 8, ARRAY_INIT_NONE);
|
|
|
|
|
wapp_array_append_capped(slang::TargetDesc, slang_targets, &target);
|
|
|
|
|
|
|
|
|
|
slang::CompilerOptionEntry entry = {};
|
|
|
|
|
entry.name = slang::CompilerOptionName::EmitSpirvDirectly;
|
|
|
|
|
entry.value = {slang::CompilerOptionValueKind::Int, 1};
|
|
|
|
|
|
|
|
|
|
SlangCompOptEntryArray slang_options = wapp_array_with_capacity(slang::CompilerOptionEntry, 8, ARRAY_INIT_NONE);
|
|
|
|
|
wapp_array_append_capped(slang::CompilerOptionEntry, slang_options, &entry);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Create Session
|
|
|
|
|
slang::SessionDesc slang_session_desc = {};
|
|
|
|
|
slang_session_desc.targets = slang_targets;
|
|
|
|
|
slang_session_desc.targetCount = wapp_array_count(slang_targets);
|
|
|
|
|
slang_session_desc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;
|
|
|
|
|
slang_session_desc.compilerOptionEntries = slang_options;
|
|
|
|
|
slang_session_desc.compilerOptionEntryCount = wapp_array_count(slang_options);
|
|
|
|
|
|
|
|
|
|
Slang::ComPtr<slang::ISession> slang_session;
|
|
|
|
|
slang_global_session->createSession(slang_session_desc, slang_session.writeRef());
|
|
|
|
|
// }}}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Load Shader Code
|
|
|
|
|
Slang::ComPtr<slang::IModule> slang_module {
|
|
|
|
|
slang_session->loadModuleFromSource("triangle", "assets/shader.slang", nullptr, nullptr),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Slang::ComPtr<slang::IBlob> spirv;
|
|
|
|
|
slang_module->getTargetCode(0, spirv.writeRef());
|
|
|
|
|
// }}}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Create Shader Module
|
|
|
|
|
VkShaderModuleCreateInfo module_create_info = {};
|
|
|
|
|
module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
|
|
|
module_create_info.codeSize = spirv->getBufferSize();
|
|
|
|
|
module_create_info.pCode = (u32 *)spirv->getBufferPointer();
|
|
|
|
|
check(vkCreateShaderModule(device, &module_create_info, NULL, &shader_module));
|
|
|
|
|
// }}}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Graphics Pipeline
|
|
|
|
|
// {{{ Create Pipeline Layout
|
|
|
|
|
// The pipeline layout defines the interface between the pipeline and our shaders. We add a push
|
|
|
|
|
// constant range and the texture descriptor set layout. A push constant range defines a range of
|
|
|
|
|
// values that we can directly push to the shader without having to go through a buffer. We use
|
|
|
|
|
// these to pass a pointer to the shader data buffer.
|
|
|
|
|
VkPushConstantRange push_constants = {};
|
|
|
|
|
push_constants.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
|
push_constants.size = sizeof(VkDeviceAddress);
|
|
|
|
|
|
|
|
|
|
VkPipelineLayoutCreateInfo pl_create_info = {};
|
|
|
|
|
pl_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
|
|
|
pl_create_info.setLayoutCount = 1;
|
|
|
|
|
pl_create_info.pSetLayouts = &desc_set_layout_tex;
|
|
|
|
|
pl_create_info.pushConstantRangeCount = 1;
|
|
|
|
|
pl_create_info.pPushConstantRanges = &push_constants;
|
|
|
|
|
|
|
|
|
|
check(vkCreatePipelineLayout(device, &pl_create_info, NULL, &graphics_pipeline_layout));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Vertex Binding And Attributes
|
|
|
|
|
VkVertexInputBindingDescription vertex_binding = {};
|
|
|
|
|
vertex_binding.binding = 0;
|
|
|
|
|
vertex_binding.stride = sizeof(Vertex);
|
|
|
|
|
vertex_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
|
|
|
|
|
|
|
|
VkVertexInputAttributeDescriptionArray vertex_attributes = wapp_array(
|
|
|
|
|
VkVertexInputAttributeDescription,
|
|
|
|
|
// Location, Binding, Format, Offset
|
|
|
|
|
VkVertexInputAttributeDescription{0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0},
|
|
|
|
|
VkVertexInputAttributeDescription{1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)},
|
|
|
|
|
VkVertexInputAttributeDescription{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
|
|
|
|
|
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
|
|
|
vertex_input_state.vertexBindingDescriptionCount = 1;
|
|
|
|
|
vertex_input_state.pVertexBindingDescriptions = &vertex_binding;
|
|
|
|
|
vertex_input_state.vertexAttributeDescriptionCount = wapp_array_count(vertex_attributes);
|
|
|
|
|
vertex_input_state.pVertexAttributeDescriptions = vertex_attributes;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Input Assembly
|
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo input_assembly_state = {};
|
|
|
|
|
input_assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
|
|
|
input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Shader Stages
|
|
|
|
|
VkPipelineShaderStageCreateInfoArray shader_stages = wapp_array_with_capacity(VkPipelineShaderStageCreateInfo, 2, ARRAY_INIT_FILLED);
|
|
|
|
|
|
|
|
|
|
// Vertex Shader
|
|
|
|
|
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
|
|
|
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
|
shader_stages[0].module = shader_module;
|
|
|
|
|
shader_stages[0].pName = "main";
|
|
|
|
|
|
|
|
|
|
// Fragment Shader
|
|
|
|
|
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
|
|
|
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
|
shader_stages[1].module = shader_module;
|
|
|
|
|
shader_stages[1].pName = "main";
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Viewport And Scissor Dynamic States
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewport_state = {};
|
|
|
|
|
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
|
|
|
viewport_state.viewportCount = 1;
|
|
|
|
|
viewport_state.scissorCount = 1;
|
|
|
|
|
|
|
|
|
|
VkDynamicStateArray dynamic_states = wapp_array(VkDynamicState, VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR);
|
|
|
|
|
|
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamic_state = {};
|
|
|
|
|
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
|
|
|
dynamic_state.dynamicStateCount = wapp_array_count(dynamic_states);
|
|
|
|
|
dynamic_state.pDynamicStates = dynamic_states;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Depth/Stencil State
|
|
|
|
|
VkPipelineDepthStencilStateCreateInfo depth_stencil_state = {};
|
|
|
|
|
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
|
|
|
|
depth_stencil_state.depthTestEnable = VK_TRUE;
|
|
|
|
|
depth_stencil_state.depthWriteEnable = VK_TRUE;
|
|
|
|
|
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Dynamic Rendering
|
|
|
|
|
VkPipelineRenderingCreateInfo rendering_create_info = {};
|
|
|
|
|
rendering_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
|
|
|
|
|
rendering_create_info.colorAttachmentCount = 1;
|
|
|
|
|
rendering_create_info.pColorAttachmentFormats = &image_format;
|
|
|
|
|
rendering_create_info.depthAttachmentFormat = depth_format;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Blending State
|
|
|
|
|
// Blending disabled
|
|
|
|
|
VkPipelineColorBlendAttachmentState blend_attachment = {};
|
|
|
|
|
blend_attachment.colorWriteMask = 0xf;
|
|
|
|
|
|
|
|
|
|
VkPipelineColorBlendStateCreateInfo blend_state = {};
|
|
|
|
|
blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
|
|
|
blend_state.attachmentCount = 1;
|
|
|
|
|
blend_state.pAttachments = &blend_attachment;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Rasterization State
|
|
|
|
|
VkPipelineRasterizationStateCreateInfo rasterization_state = {};
|
|
|
|
|
rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
|
|
|
rasterization_state.lineWidth = 1.0f;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Multisampling State
|
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisampling_state = {};
|
|
|
|
|
multisampling_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
|
|
|
multisampling_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Create Graphics Pipeline
|
|
|
|
|
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info = {};
|
|
|
|
|
graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
|
|
|
|
graphics_pipeline_create_info.pNext = &rendering_create_info;
|
|
|
|
|
graphics_pipeline_create_info.stageCount = wapp_array_count(shader_stages);
|
|
|
|
|
graphics_pipeline_create_info.pStages = shader_stages;
|
|
|
|
|
graphics_pipeline_create_info.pVertexInputState = &vertex_input_state;
|
|
|
|
|
graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_state;
|
|
|
|
|
graphics_pipeline_create_info.pViewportState = &viewport_state;
|
|
|
|
|
graphics_pipeline_create_info.pRasterizationState = &rasterization_state;
|
|
|
|
|
graphics_pipeline_create_info.pMultisampleState = &multisampling_state;
|
|
|
|
|
graphics_pipeline_create_info.pDepthStencilState = &depth_stencil_state;
|
|
|
|
|
graphics_pipeline_create_info.pColorBlendState = &blend_state;
|
|
|
|
|
graphics_pipeline_create_info.pDynamicState = &dynamic_state;
|
|
|
|
|
graphics_pipeline_create_info.layout = graphics_pipeline_layout;
|
|
|
|
|
|
|
|
|
|
check(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, NULL, &graphics_pipeline));
|
|
|
|
|
// }}}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Render Loop
|
|
|
|
|
SDL_Event event = {};
|
|
|
|
|
/**
|
|
|
|
|
* Render loop overview:
|
|
|
|
|
* - Wait on fence
|
|
|
|
|
* - Acquire next image
|
|
|
|
|
* - Update shader data
|
|
|
|
|
* - Record command buffer
|
|
|
|
|
* - Submit command buffer
|
|
|
|
|
* - Present image
|
|
|
|
|
* - Poll events
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
object_rotations = wapp_array_with_capacity(glm::vec3, instance_count, ARRAY_INIT_FILLED);
|
|
|
|
|
u64 last_time = SDL_GetTicks();
|
|
|
|
|
SDL_Event event = {};
|
|
|
|
|
|
|
|
|
|
while (running) {
|
|
|
|
|
// {{{ Wait On Fence
|
|
|
|
|
check(vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX));
|
|
|
|
|
check(vkResetFences(device, 1, &fences[frame_index]));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Acquire Next Swapchain Image
|
|
|
|
|
check_swapchain(vkAcquireNextImageKHR(device, swapchain, UINT64_MAX,
|
|
|
|
|
image_acquired_semaphores[frame_index], VK_NULL_HANDLE,
|
|
|
|
|
&image_index));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Update Shader Data
|
|
|
|
|
shader_data.projection = glm::perspective(glm::radians(45.0f), (f32)window_size.x / (f32)window_size.y,
|
|
|
|
|
0.1f, 32.0f);
|
|
|
|
|
shader_data.view = glm::translate(glm::mat4(1.0f), camera_position);
|
|
|
|
|
for (i32 i = 0; i < instance_count; ++i) {
|
|
|
|
|
glm::vec3 instance_pos = glm::vec3((f32)(i - 1) * 3.0f, 0.0f, 0.0f);
|
|
|
|
|
shader_data.model[i] = glm::translate(glm::mat4(1.0f), instance_pos) *
|
|
|
|
|
glm::mat4_cast(glm::quat(object_rotations[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(shader_data_bufs[frame_index].allocation_info.pMappedData, &shader_data, sizeof(ShaderData));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Record Command Buffer
|
|
|
|
|
VkCommandBuffer cb = command_buffers[frame_index];
|
|
|
|
|
|
|
|
|
|
check(vkResetCommandBuffer(cb, 0));
|
|
|
|
|
|
|
|
|
|
VkCommandBufferBeginInfo cmd_begin_info = {};
|
|
|
|
|
cmd_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
|
cmd_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
|
|
|
check(vkBeginCommandBuffer(cb, &cmd_begin_info));
|
|
|
|
|
|
|
|
|
|
VkImageMemoryBarrier2Array memory_barriers = wapp_array_with_capacity(VkImageMemoryBarrier2, 2, ARRAY_INIT_FILLED);
|
|
|
|
|
|
|
|
|
|
// Color Attachment
|
|
|
|
|
memory_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
memory_barriers[0].srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
memory_barriers[0].srcAccessMask = 0;
|
|
|
|
|
memory_barriers[0].dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
memory_barriers[0].dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
memory_barriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
memory_barriers[0].newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
|
|
|
|
memory_barriers[0].image = swapchain_images[image_index];
|
|
|
|
|
memory_barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
memory_barriers[0].subresourceRange.levelCount = 1;
|
|
|
|
|
memory_barriers[0].subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
// Depth Attachment
|
|
|
|
|
memory_barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
memory_barriers[1].srcStageMask = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT;
|
|
|
|
|
memory_barriers[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
memory_barriers[1].dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT;
|
|
|
|
|
memory_barriers[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
memory_barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
memory_barriers[1].newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
|
|
|
|
memory_barriers[1].image = depth_image;
|
|
|
|
|
memory_barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
|
memory_barriers[1].subresourceRange.levelCount = 1;
|
|
|
|
|
memory_barriers[1].subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
// {{{ Transition Layout Color And Depth Images
|
|
|
|
|
VkDependencyInfo barrier_dependency_info = {};
|
|
|
|
|
barrier_dependency_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
|
|
|
|
barrier_dependency_info.imageMemoryBarrierCount = wapp_array_count(memory_barriers);
|
|
|
|
|
barrier_dependency_info.pImageMemoryBarriers = memory_barriers;
|
|
|
|
|
|
|
|
|
|
vkCmdPipelineBarrier2(cb, &barrier_dependency_info);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Dynamic Rendering Commands
|
|
|
|
|
VkClearValue color_clear_value = {};
|
|
|
|
|
color_clear_value.color = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
|
|
|
VkClearValue depth_clear_value = {};
|
|
|
|
|
depth_clear_value.depthStencil = { 1.0f, 0 };
|
|
|
|
|
|
|
|
|
|
VkRenderingAttachmentInfo color_attachment = {};
|
|
|
|
|
color_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
|
|
|
|
color_attachment.imageView = swapchain_views[image_index];
|
|
|
|
|
color_attachment.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
|
|
|
|
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
|
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
|
color_attachment.clearValue = color_clear_value;
|
|
|
|
|
|
|
|
|
|
VkRenderingAttachmentInfo depth_attachment = {};
|
|
|
|
|
depth_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
|
|
|
|
depth_attachment.imageView = depth_view;
|
|
|
|
|
depth_attachment.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
|
|
|
|
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
|
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
|
|
|
depth_attachment.clearValue = depth_clear_value;
|
|
|
|
|
|
|
|
|
|
VkRect2D render_area = {};
|
|
|
|
|
render_area.extent = { (u32)window_size.x, (u32)window_size.y };
|
|
|
|
|
|
|
|
|
|
VkRenderingInfo render_info = {};
|
|
|
|
|
render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
|
|
|
|
render_info.renderArea = render_area;
|
|
|
|
|
render_info.layerCount = 1;
|
|
|
|
|
render_info.colorAttachmentCount = 1;
|
|
|
|
|
render_info.pColorAttachments = &color_attachment;
|
|
|
|
|
render_info.pDepthAttachment = &depth_attachment;
|
|
|
|
|
vkCmdBeginRendering(cb, &render_info);
|
|
|
|
|
|
|
|
|
|
// {{{ Set Viewport And Scissor Dynamic States
|
|
|
|
|
VkViewport viewport = {};
|
|
|
|
|
viewport.width = window_size.x;
|
|
|
|
|
viewport.height = window_size.y;
|
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
|
vkCmdSetViewport(cb, 0, 1, &viewport);
|
|
|
|
|
|
|
|
|
|
VkRect2D scissor = {};
|
|
|
|
|
scissor.extent = render_area.extent;
|
|
|
|
|
vkCmdSetScissor(cb, 0, 1, &scissor);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Bind Resources
|
|
|
|
|
vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
|
|
|
|
vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, 1, &desc_set_tex, 0, NULL);
|
|
|
|
|
|
|
|
|
|
VkDeviceSize vertex_offset = {};
|
|
|
|
|
vkCmdBindVertexBuffers(cb, 0, 1, &vert_index_buf, &vertex_offset);
|
|
|
|
|
vkCmdBindIndexBuffer(cb, vert_index_buf, vertex_buf_size, VK_INDEX_TYPE_UINT16);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Send ShaderData Buffer Device Address To Shader
|
|
|
|
|
vkCmdPushConstants(cb, graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0,
|
|
|
|
|
sizeof(VkDeviceAddress), &shader_data_bufs[frame_index].device_address);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Draw Commands
|
|
|
|
|
vkCmdDrawIndexed(cb, index_count, instance_count, 0, 0, 0);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
vkCmdEndRendering(cb);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Transition Color Attachment For Presentation
|
|
|
|
|
VkImageMemoryBarrier2 present_barrier = {};
|
|
|
|
|
present_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
|
|
|
|
present_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
present_barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
|
present_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
present_barrier.dstAccessMask = 0;
|
|
|
|
|
present_barrier.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
|
|
|
|
present_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
|
present_barrier.image = swapchain_images[image_index];
|
|
|
|
|
present_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
present_barrier.subresourceRange.levelCount = 1;
|
|
|
|
|
present_barrier.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
VkDependencyInfo present_dependency_info = {};
|
|
|
|
|
present_dependency_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
|
|
|
|
present_dependency_info.imageMemoryBarrierCount = 1;
|
|
|
|
|
present_dependency_info.pImageMemoryBarriers = &present_barrier;
|
|
|
|
|
vkCmdPipelineBarrier2(cb, &present_dependency_info);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
check(vkEndCommandBuffer(cb));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Submit Command Buffer
|
|
|
|
|
VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
|
|
|
|
|
|
// The wait semaphore in pWaitSemaphores ensures that execution of the command buffer does not
|
|
|
|
|
// begin until the swapchain image we want to render to has been acquired. This means
|
|
|
|
|
// presentation has finished and the image has been released by the presentation engine. This is
|
|
|
|
|
// required because swapchain images are owned by the presentation engine rather than by our
|
|
|
|
|
// application. The pipeline stage specified in pWaitDstStageMask makes that wait happen at the
|
|
|
|
|
// color attachment output stage, so in theory the GPU may already begin work on earlier
|
|
|
|
|
// pipeline stages, such as vertex fetching. The signal semaphore in pSignalSemaphores, on the
|
|
|
|
|
// other hand, is signaled by the GPU once command buffer execution has completed and ensures
|
|
|
|
|
// that presentation does not begin until the command buffer has finished execution. Together,
|
|
|
|
|
// these guarantees prevent read/write hazards that could cause the GPU to read from or write
|
|
|
|
|
// to resources that are still in use.
|
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
|
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
|
submit_info.waitSemaphoreCount = 1;
|
|
|
|
|
submit_info.pWaitSemaphores = &image_acquired_semaphores[frame_index];
|
|
|
|
|
submit_info.pWaitDstStageMask = &wait_stages;
|
|
|
|
|
submit_info.commandBufferCount = 1;
|
|
|
|
|
submit_info.pCommandBuffers = &cb;
|
|
|
|
|
submit_info.signalSemaphoreCount = 1;
|
|
|
|
|
submit_info.pSignalSemaphores = &render_completed_semaphores[image_index];
|
|
|
|
|
|
|
|
|
|
check(vkQueueSubmit(queue, 1, &submit_info, fences[frame_index]));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
frame_index = (frame_index + 1) % max_frames_in_flight;
|
|
|
|
|
|
|
|
|
|
// {{{ Present Image
|
|
|
|
|
VkPresentInfoKHR present_info = {};
|
|
|
|
|
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
|
|
|
|
present_info.waitSemaphoreCount = 1;
|
|
|
|
|
present_info.pWaitSemaphores = &render_completed_semaphores[image_index];
|
|
|
|
|
present_info.swapchainCount = 1;
|
|
|
|
|
present_info.pSwapchains = &swapchain;
|
|
|
|
|
present_info.pImageIndices = &image_index;
|
|
|
|
|
|
|
|
|
|
check_swapchain(vkQueuePresentKHR(queue, &present_info));
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Poll Events
|
|
|
|
|
f32 elapsed_time = (SDL_GetTicks() - last_time) / 1000.0f;
|
|
|
|
|
last_time = SDL_GetTicks();
|
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
|
|
|
switch (event.type) {
|
|
|
|
|
case SDL_EVENT_QUIT:
|
|
|
|
@@ -822,14 +1345,125 @@ int main() {
|
|
|
|
|
case SDL_EVENT_KEY_DOWN:
|
|
|
|
|
if (event.key.key == SDLK_ESCAPE) {
|
|
|
|
|
running = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Select active model instance
|
|
|
|
|
if (event.key.key == SDLK_PLUS || event.key.key == SDLK_KP_PLUS || event.key.key == SDLK_EQUALS) {
|
|
|
|
|
shader_data.selected = (shader_data.selected < 2) ? shader_data.selected + 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
if (event.key.key == SDLK_MINUS || event.key.key == SDLK_KP_MINUS) {
|
|
|
|
|
shader_data.selected = (shader_data.selected > 0) ? shader_data.selected - 1 : 2;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
// Rotate the selected object with mouse drag
|
|
|
|
|
case SDL_EVENT_MOUSE_MOTION:
|
|
|
|
|
if (event.button.button == SDL_BUTTON_LEFT) {
|
|
|
|
|
object_rotations[shader_data.selected].x -= (float)event.motion.yrel * elapsed_time;
|
|
|
|
|
object_rotations[shader_data.selected].y += (float)event.motion.xrel * elapsed_time;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
// Zooming with the mouse wheel
|
|
|
|
|
case SDL_EVENT_MOUSE_WHEEL:
|
|
|
|
|
camera_position.z += (float)event.wheel.y * elapsed_time * 10.0f;
|
|
|
|
|
break;
|
|
|
|
|
// Window resize
|
|
|
|
|
case SDL_EVENT_WINDOW_RESIZED:
|
|
|
|
|
update_swapchain = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Recreate Swapchain If Needed
|
|
|
|
|
if (update_swapchain) {
|
|
|
|
|
// TODO (Abdelrahman): This doesn't work well. Debug it.
|
|
|
|
|
update_swapchain = false;
|
|
|
|
|
check(vkDeviceWaitIdle(device));
|
|
|
|
|
|
|
|
|
|
check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_caps));
|
|
|
|
|
|
|
|
|
|
// Create new swapchain
|
|
|
|
|
swapchain_create_info.oldSwapchain = swapchain;
|
|
|
|
|
swapchain_create_info.imageExtent = { (u32)window_size.x, (u32)window_size.y };
|
|
|
|
|
check(vkCreateSwapchainKHR(device, &swapchain_create_info, NULL, &swapchain));
|
|
|
|
|
|
|
|
|
|
// Clean old image views
|
|
|
|
|
for (u32 i = 0; i < swapchain_image_count; ++i) {
|
|
|
|
|
vkDestroyImageView(device, swapchain_views[i], NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clean old render_completed semaphores
|
|
|
|
|
for (u32 i = 0; i < swapchain_image_count; ++i) {
|
|
|
|
|
vkDestroySemaphore(device, render_completed_semaphores[i], NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get new swapchain images
|
|
|
|
|
check(vkGetSwapchainImagesKHR(device, swapchain, &swapchain_image_count, nullptr));
|
|
|
|
|
if (swapchain_image_count != wapp_array_count(swapchain_images)) {
|
|
|
|
|
swapchain_images = wapp_array_alloc_capacity(VkImage, &arena, swapchain_image_count, ARRAY_INIT_FILLED);
|
|
|
|
|
swapchain_views = wapp_array_alloc_capacity(VkImageView, &arena, swapchain_image_count, ARRAY_INIT_FILLED);
|
|
|
|
|
render_completed_semaphores = wapp_array_alloc_capacity(VkSemaphore, &arena, swapchain_image_count, ARRAY_INIT_FILLED);
|
|
|
|
|
}
|
|
|
|
|
check(swapchain_images != nullptr, EXIT_CODE_ALLOCATION_FAILURE);
|
|
|
|
|
check(vkGetSwapchainImagesKHR(device, swapchain, &swapchain_image_count, swapchain_images));
|
|
|
|
|
|
|
|
|
|
check(swapchain_views != nullptr, EXIT_CODE_ALLOCATION_FAILURE);
|
|
|
|
|
|
|
|
|
|
for (u32 i = 0; i < swapchain_image_count; ++i) {
|
|
|
|
|
VkImageViewCreateInfo view_create_info = {};
|
|
|
|
|
view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
|
|
|
view_create_info.image = swapchain_images[i];
|
|
|
|
|
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
|
view_create_info.format = image_format;
|
|
|
|
|
view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
view_create_info.subresourceRange.levelCount = 1;
|
|
|
|
|
view_create_info.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
check(vkCreateImageView(device, &view_create_info, nullptr, &swapchain_views[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check(render_completed_semaphores != nullptr, EXIT_CODE_SYNC_OBJ_CREATE_FAILED);
|
|
|
|
|
|
|
|
|
|
for (u32 i = 0; i < swapchain_image_count; ++i) {
|
|
|
|
|
check(vkCreateSemaphore(device, &semaphore_create_info, NULL, &render_completed_semaphores[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vkDestroySwapchainKHR(device, swapchain_create_info.oldSwapchain, NULL);
|
|
|
|
|
vmaDestroyImage(allocator, depth_image, depth_allocation);
|
|
|
|
|
vkDestroyImageView(device, depth_view, NULL);
|
|
|
|
|
|
|
|
|
|
depth_image_create_info.extent = { (u32)window_size.x, (u32)window_size.y, 1 };
|
|
|
|
|
|
|
|
|
|
VmaAllocationCreateInfo alloc_create_info = {};
|
|
|
|
|
alloc_create_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
|
|
|
|
alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO;
|
|
|
|
|
|
|
|
|
|
check(vmaCreateImage(allocator, &depth_image_create_info, &alloc_create_info, &depth_image,
|
|
|
|
|
&depth_allocation, nullptr));
|
|
|
|
|
|
|
|
|
|
VkImageViewCreateInfo view_create_info = {};
|
|
|
|
|
view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
|
|
|
view_create_info.image = depth_image;
|
|
|
|
|
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
|
view_create_info.format = depth_format;
|
|
|
|
|
view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
|
view_create_info.subresourceRange.levelCount = 1;
|
|
|
|
|
view_create_info.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
check(vkCreateImageView(device, &view_create_info, NULL, &depth_view));
|
|
|
|
|
}
|
|
|
|
|
// }}}
|
|
|
|
|
}
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// {{{ Cleanup
|
|
|
|
|
check(vkDeviceWaitIdle(device));
|
|
|
|
|
|
|
|
|
|
vkDestroyPipeline(device, graphics_pipeline, NULL);
|
|
|
|
|
vkDestroyPipelineLayout(device, graphics_pipeline_layout, NULL);
|
|
|
|
|
vkDestroyShaderModule(device, shader_module, NULL);
|
|
|
|
|
vkDestroyDescriptorPool(device, desc_pool, NULL);
|
|
|
|
|
vkDestroyDescriptorSetLayout(device, desc_set_layout_tex, NULL);
|
|
|
|
|
for (u32 i = 0; i < wapp_array_count(textures); ++i) {
|
|
|
|
|
vkDestroySampler(device, textures[i].sampler, NULL);
|
|
|
|
|
vkDestroyImageView(device, textures[i].view, NULL);
|
|
|
|
@@ -865,7 +1499,7 @@ int main() {
|
|
|
|
|
wapp_mem_arena_allocator_destroy(&arena);
|
|
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
return EXIT_CODE_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// {{{ Helper Functions
|
|
|
|
|