From 0a882c4b85423a170455f05a52d18f7fa7c9220b Mon Sep 17 00:00:00 2001 From: Quentin LADEVIE Date: Sun, 22 Jan 2023 17:59:24 +0100 Subject: Api test bindless and command buffers --- api-proto.txt | 7 +- api-test/CMakeLists.txt | 1 + api-test/command-buffer.cpp | 123 +++++++++--- api-test/main.cpp | 101 +++++++--- api-test/vk-bindless.cpp | 218 +++++++++++++++++++++ api-test/vk-device.cpp | 367 +++++++++++++++++++++++++++-------- include/api-test/buffer.hpp | 7 - include/api-test/command-buffer.hpp | 37 ++-- include/api-test/freelist.hpp | 43 +++- include/api-test/texture.hpp | 12 -- include/api-test/vk-bindless.hpp | 61 ++++++ include/api-test/vk-device-types.hpp | 70 +++++++ include/api-test/vk-device.hpp | 138 +++++++------ include/api-test/vk-utils.hpp | 11 ++ include/vec3.hpp | 16 +- include/vulkan-loader.hpp | 1 + shaders/CMakeLists.txt | 3 +- shaders/test.comp | 18 ++ 18 files changed, 996 insertions(+), 238 deletions(-) create mode 100644 api-test/vk-bindless.cpp create mode 100644 include/api-test/vk-bindless.hpp create mode 100644 include/api-test/vk-device-types.hpp create mode 100644 include/api-test/vk-utils.hpp create mode 100644 shaders/test.comp diff --git a/api-proto.txt b/api-proto.txt index a8ca1ff..8ffd5ef 100644 --- a/api-proto.txt +++ b/api-proto.txt @@ -9,6 +9,8 @@ class device_command_buffer { void stop(); VkCommandBuffer handle; + queue owner; + CMD_BUF_TYPE buf_type = CMD_BUF_TYPE::MAX; } class graphics_device_command_buffer : device_command_buffer { @@ -63,7 +65,10 @@ class device { vkAllocate(ptr, count, queue[type]); } - fence submit(span cmd_buffers, CMD_BUF_TYPE type); + submit(span cmd_buffers, semaphore) { + + vkSubmit(); + } wait(fence); wait_idle(); diff --git a/api-test/CMakeLists.txt b/api-test/CMakeLists.txt index 849f242..402ae10 100644 --- a/api-test/CMakeLists.txt +++ b/api-test/CMakeLists.txt @@ -3,6 +3,7 @@ add_executable( main.cpp vk-device.cpp command-buffer.cpp + vk-bindless.cpp ${CMAKE_SOURCE_DIR}/src/window.cpp ${CMAKE_SOURCE_DIR}/src/utils.cpp ${CMAKE_SOURCE_DIR}/src/vulkan-loader.cpp diff --git a/api-test/command-buffer.cpp b/api-test/command-buffer.cpp index 9e37f95..8d2d8c7 100644 --- a/api-test/command-buffer.cpp +++ b/api-test/command-buffer.cpp @@ -1,54 +1,117 @@ #include "command-buffer.hpp" #include +#include #include "vulkan-loader.hpp" -#include"vk-device.hpp" - -#ifdef _DEBUG -#define VKCHECK(result) assert(result == VK_SUCCESS); -#else -#define VKCHECK(result) result; -#endif +#include "vk-device.hpp" +#include "vk-utils.hpp" void command_buffer::start() const { - VkCommandBufferBeginInfo begin_info { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = nullptr, - .flags = 0U, - .pInheritanceInfo = nullptr + VkCommandBufferBeginInfo begin_info{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = 0U, + .pInheritanceInfo = nullptr }; - VKCHECK(vkBeginCommandBuffer(handle, &begin_info)) + VKCHECK(vkBeginCommandBuffer(vk_command_buffer, &begin_info)); + + const auto& bindless = vkdevice::get_device()->get_bindingmodel(); + if (queue_type == QueueType::GRAPHICS) { + vkCmdBindDescriptorSets(vk_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bindless.layout, 0, BindlessSetType::MAX, bindless.sets, 0, nullptr); + vkCmdBindDescriptorSets(vk_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, bindless.layout, 0, BindlessSetType::MAX, bindless.sets, 0, nullptr); + } else if (queue_type == QueueType::ASYNC_COMPUTE) { + vkCmdBindDescriptorSets(vk_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, bindless.layout, 0, BindlessSetType::MAX, bindless.sets, 0, nullptr); + } } void command_buffer::stop() const { - VKCHECK(vkEndCommandBuffer(handle)) + VKCHECK(vkEndCommandBuffer(vk_command_buffer)); } -void transfer_command_buffer::copy(::handle buffer_handle, ::handle texture_handle) { - const auto& buffer = vkdevice::get_buffer(buffer_handle); - const auto& texture = vkdevice::get_texture(texture_handle); - VkImageSubresourceLayers image_subresource_layers { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, +void command_buffer::barrier(handle texture_handle, VkPipelineStageFlags2 stage, VkAccessFlags2 access, VkImageLayout layout) const { + auto& texture = vkdevice::get_device()->get_texture(texture_handle); + VkImageAspectFlags aspect_mask = ((texture.desc.usages & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) + ? VK_IMAGE_ASPECT_DEPTH_BIT + : VK_IMAGE_ASPECT_COLOR_BIT; + + VkImageSubresourceRange subresource_range{ + .aspectMask = aspect_mask, + .baseMipLevel = 0U, + .levelCount = 1U, + .baseArrayLayer = 0U, + .layerCount = 1U, + }; + + VkImageMemoryBarrier2 image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = texture.stage, + .srcAccessMask = texture.access, + .dstStageMask = stage, + .dstAccessMask = access, + .oldLayout = texture.layout, + .newLayout = layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = texture.vk_image, + .subresourceRange = subresource_range, + }; + + VkDependencyInfo dependency_info{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + .memoryBarrierCount = 0U, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = 0U, + .pBufferMemoryBarriers = nullptr, + .imageMemoryBarrierCount = 1U, + .pImageMemoryBarriers = &image_barrier, + }; + vkCmdPipelineBarrier2(vk_command_buffer, &dependency_info); + + texture.layout = layout; + texture.stage = stage; + texture.access = access; +} + +void command_buffer::copy(handle buffer_handle, ::handle texture_handle, VkDeviceSize offset) const { + auto* device = vkdevice::get_device(); + const auto& buffer = device->get_buffer(buffer_handle); + const auto& texture = device->get_texture(texture_handle); + VkImageAspectFlags aspect_mask = ((texture.desc.usages & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) + ? VK_IMAGE_ASPECT_DEPTH_BIT + : VK_IMAGE_ASPECT_COLOR_BIT; + VkImageSubresourceLayers image_subresource_layers{ + .aspectMask = aspect_mask, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }; - VkBufferImageCopy buffer_to_image_copy { - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = image_subresource_layers, - .imageOffset = { .x = 0, .y = 0, .z = 0 }, - .imageExtent = { - .width = texture.desc.width, - .height = texture.desc.height, - .depth = texture.desc.depth, + VkBufferImageCopy buffer_to_image_copy{ + .bufferOffset = offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = image_subresource_layers, + .imageOffset = { .x = 0, .y = 0, .z = 0 }, + .imageExtent = { + .width = texture.desc.width, + .height = texture.desc.height, + .depth = texture.desc.depth, }, }; - vkCmdCopyBufferToImage(handle, buffer.handle, texture.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_to_image_copy); + vkCmdCopyBufferToImage(vk_command_buffer, buffer.vk_buffer, texture.vk_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_to_image_copy); +} + +void graphics_command_buffer::dispatch(const dispatch_params& params) { + const auto& pipeline = vkdevice::get_device()->get_pipeline(params.pipeline); + + vkCmdBindPipeline(vk_command_buffer, pipeline.bind_point, pipeline.vk_pipeline); + + vkCmdDispatch(vk_command_buffer, params.group_size.x / params.local_group_size.x, params.group_size.y / params.local_group_size.y, params.group_size.z / params.local_group_size.z); } diff --git a/api-test/main.cpp b/api-test/main.cpp index 40f5c72..4ac68ad 100644 --- a/api-test/main.cpp +++ b/api-test/main.cpp @@ -1,10 +1,14 @@ #include #include +#include #include +#define STB_IMAGE_IMPLEMENTATION +#include + #include "window.hpp" #include "vk-device.hpp" -// #include "resource-manager.hpp" +#include "utils.hpp" #define ENABLE_RENDERDOC @@ -26,50 +30,97 @@ int main() { } #endif - window wnd { 1280U, 720U }; + window wnd { 640U, 360U }; #ifdef ENABLE_RENDERDOC if(rdoc_api != nullptr) rdoc_api->StartFrameCapture(nullptr, nullptr); #endif - vkdevice device { wnd }; + auto* device = vkdevice::init_device(wnd); - auto gpu_texture_handle = device.create_texture({ - .width = 1024U, - .height = 1024U, - .usages = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + constexpr size_t image_size = 1024U; + constexpr size_t image_count = 4U; + auto staging_buffer_handle = device->create_buffer({ + .size = image_size * image_size * sizeof(uint32_t) * image_count, + .usages = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .memory_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + .memory_usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + }); + const auto& staging_buffer = device->get_buffer(staging_buffer_handle); + + int width; + int height; + int channels; + uint8_t* data = stbi_load("../models/sponza/466164707995436622.jpg", &width, &height, &channels, 4); + size_t size = static_cast(width) * height * 4U; + memcpy(staging_buffer.mapped_ptr, data, size); + + auto gpu_texture_handle = device->create_texture({ + .width = static_cast(width), + .height = static_cast(height), + .usages = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT, .format = VK_FORMAT_R8G8B8A8_UNORM, - .type = VK_IMAGE_TYPE_2D, + .type = VK_IMAGE_TYPE_2D, }); - auto gpu_buffer = device.get_buffer(device.create_buffer({ - .size = 1024U * sizeof(uint32_t), - .usages = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - .memory_usage = VMA_MEMORY_USAGE_GPU_ONLY, - })); + off_t offset = width * height * 4; + data = stbi_load("../models/sponza/715093869573992647.jpg", &width, &height, &channels, 4); + size = static_cast(width) * height * 4U; + auto* addr = static_cast(static_cast(staging_buffer.mapped_ptr) + offset); + memcpy(addr, data, size); - auto staging_buffer_handle = device.create_buffer({ - .size = 1024U * sizeof(uint32_t), - .usages = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - .memory_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - .memory_usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + auto second_texture = device->create_texture({ + .width = static_cast(width), + .height = static_cast(height), + .usages = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .type = VK_IMAGE_TYPE_2D, }); - auto staging_buffer = device.get_buffer(staging_buffer_handle); - uint64_t t = 7; - (*(uint64_t*)staging_buffer.mapped_ptr) = t; + auto output_texture_handle = device->create_texture({ + .width = static_cast(width), + .height = static_cast(height), + .usages = VK_IMAGE_USAGE_STORAGE_BIT, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .type = VK_IMAGE_TYPE_2D, + }); + + // auto gpu_buffer = vkdevice::get_buffer(device.create_buffer({ + // .size = 1024U * sizeof(uint32_t), + // .usages = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + // .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + // .memory_usage = VMA_MEMORY_USAGE_GPU_ONLY, + // })); + + auto code = read_file("shaders/test.comp.spv"); + auto compute = device->create_pipeline({ + .cs_code = std::span(code), + }); - transfer_command_buffer command_buffer; - device.allocate_command_buffers(&command_buffer, 1, QueueType::GRAPHICS); + + graphics_command_buffer command_buffer; + device->allocate_command_buffers(&command_buffer, 1, QueueType::GRAPHICS); command_buffer.start(); + command_buffer.barrier(gpu_texture_handle, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + command_buffer.barrier(second_texture, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); command_buffer.copy(staging_buffer_handle, gpu_texture_handle); + command_buffer.copy(staging_buffer_handle, second_texture, static_cast(offset)); + + command_buffer.barrier(gpu_texture_handle, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + command_buffer.barrier(second_texture, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL); + command_buffer.barrier(output_texture_handle, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + command_buffer.dispatch({ + .pipeline = compute, + .group_size = { image_size, image_size, 1U }, + .local_group_size = { 8U, 8U, 1U }, + }); command_buffer.stop(); - device.submit(&command_buffer, 1); + device->submit(&command_buffer, 1); #ifdef ENABLE_RENDERDOC if(rdoc_api != nullptr) rdoc_api->EndFrameCapture(nullptr, nullptr); diff --git a/api-test/vk-bindless.cpp b/api-test/vk-bindless.cpp new file mode 100644 index 0000000..d1dbf03 --- /dev/null +++ b/api-test/vk-bindless.cpp @@ -0,0 +1,218 @@ +#include "vk-bindless.hpp" + +#include "vk-utils.hpp" +#include "vulkan-loader.hpp" + +bindless_model bindless_model::create_bindless_model(VkDevice device) { + bindless_model bindless; +#ifndef USE_VK_DESCRIPTOR_BUFFER + bindless.create_immutable_samplers(device); + bindless.create_layout(device); + bindless.create_descriptor_pool(device); + bindless.allocate_sets(device); +#else + VkBufferCreateInfo create_info{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .size = desc.size, + .usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + VmaAllocatorCreateFlags alloc_create_flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VmaAllocationCreateInfo alloc_create_info{ + .flags = alloc_create_flags, + .usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + .requiredFlags = desc.memory_properties, + .preferredFlags = 0U, + .memoryTypeBits = 0U, + .pool = nullptr, + .pUserData = nullptr, + }; + + VmaAllocationInfo alloc_info{}; + + VKCHECK(vmaCreateBuffer(gpu_allocator, &create_info, &alloc_create_info, &device_buffer.vk_buffer, &device_buffer.alloc, &alloc_info)); + + device_buffer.mapped_ptr = alloc_info.pMappedData; + + return { buffers.add(device_buffer) }; +#endif + + return bindless; +} + +void bindless_model::destroy_bindless_model(VkDevice device, bindless_model& bindless) { + vkDestroyDescriptorPool(device, bindless.descriptor_pool, nullptr); + + for (auto index{ 0U }; index < BindlessSetType::MAX; index++) { + vkDestroyDescriptorSetLayout(device, bindless.sets_layout[index], nullptr); + } + + vkDestroyPipelineLayout(device, bindless.layout, nullptr); + + bindless.destroy_immutable_samplers(device); +} + +void bindless_model::create_layout(VkDevice device) { + // Set 0 global resources + // Binding 0 Storage images + // Binding 1 Sampled images + // Binding 2 Samplers + { + VkDescriptorSetLayoutBinding set_layout_bindings[]{ + { + .binding = 0U, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = SamplerType::MAX_SAMPLER, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = samplers, + }, + { + .binding = 1U, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = set_descriptors_count[BindlessSetType::GLOBAL], + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = 2U, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = set_descriptors_count[BindlessSetType::GLOBAL], + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr, + }, + }; + + VkDescriptorBindingFlags flags[]{ + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, + }; + + VkDescriptorSetLayoutBindingFlagsCreateInfo layout_binding_flags{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .pNext = VK_NULL_HANDLE, + .bindingCount = sizeof(flags) / sizeof(VkDescriptorBindingFlags), + .pBindingFlags = flags, + }; + + VkDescriptorSetLayoutCreateInfo create_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = &layout_binding_flags, + .flags = 0U, + .bindingCount = sizeof(set_layout_bindings) / sizeof(VkDescriptorSetLayoutBinding), + .pBindings = set_layout_bindings, + }; + + VKCHECK(vkCreateDescriptorSetLayout(device, &create_info, nullptr, &sets_layout[BindlessSetType::GLOBAL])); + } + + VkPipelineLayoutCreateInfo create_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .setLayoutCount = sizeof(sets_layout) / sizeof(VkDescriptorSetLayout), + .pSetLayouts = sets_layout, + .pushConstantRangeCount = 0U, + .pPushConstantRanges = nullptr, + }; + + VKCHECK(vkCreatePipelineLayout(device, &create_info, nullptr, &layout)); +} + +#ifndef USE_VK_DESCRIPTOR_BUFFER +void bindless_model::create_descriptor_pool(VkDevice device) { + VkDescriptorPoolSize pool_sizes[]{ + { + .type = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = set_descriptors_count[BindlessSetType::MAX], + }, + { + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = set_descriptors_count[BindlessSetType::MAX], + }, + { + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = set_descriptors_count[BindlessSetType::MAX], + }, + }; + + VkDescriptorPoolCreateInfo descriptor_pool_create_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .maxSets = descriptor_pool_allocable_sets_count, + .poolSizeCount = sizeof(pool_sizes) / sizeof(VkDescriptorPoolSize), + .pPoolSizes = pool_sizes, + }; + VKCHECK(vkCreateDescriptorPool(device, &descriptor_pool_create_info, nullptr, &descriptor_pool)); +} + +void bindless_model::allocate_sets(VkDevice device) { + VkDescriptorSetAllocateInfo allocate_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = descriptor_pool, + .descriptorSetCount = 1U, + .pSetLayouts = &sets_layout[BindlessSetType::GLOBAL], + }; + VKCHECK(vkAllocateDescriptorSets(device, &allocate_info, &sets[BindlessSetType::GLOBAL])); +} + +void bindless_model::create_immutable_samplers(VkDevice device) { + VkSamplerCreateInfo create_info{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = VK_NULL_HANDLE, + .flags = 0, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .mipLodBias = 0.f, + .anisotropyEnable = VK_FALSE, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .minLod = 0.f, + .maxLod = 0.f, + .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }; + + // Nearest sampler creation + { + VkFilter filter = VK_FILTER_NEAREST; + VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + create_info.magFilter = filter, + create_info.minFilter = filter, + create_info.addressModeU = address_mode, + create_info.addressModeV = address_mode, + create_info.addressModeW = address_mode, + + VKCHECK(vkCreateSampler(device, &create_info, VK_NULL_HANDLE, &samplers[SamplerType::NEAREST])); + } + + // Linear sampler creation + { + VkFilter filter = VK_FILTER_LINEAR; + VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + create_info.magFilter = filter, + create_info.minFilter = filter, + create_info.addressModeU = address_mode, + create_info.addressModeV = address_mode, + create_info.addressModeW = address_mode, + + VKCHECK(vkCreateSampler(device, &create_info, VK_NULL_HANDLE, &samplers[SamplerType::LINEAR])); + } +} + +void bindless_model::destroy_immutable_samplers(VkDevice device) { + for (auto* sampler : samplers) { + vkDestroySampler(device, sampler, nullptr); + } +} +#else +#endif diff --git a/api-test/vk-device.cpp b/api-test/vk-device.cpp index ef57f01..404cd43 100644 --- a/api-test/vk-device.cpp +++ b/api-test/vk-device.cpp @@ -9,12 +9,9 @@ #include "vulkan-loader.hpp" #include "window.hpp" +#include "vk-utils.hpp" -#ifdef _DEBUG -#define VKCHECK(result) assert(result == VK_SUCCESS); -#else -#define VKCHECK(result) result; -#endif +vkdevice* vkdevice::render_device = nullptr; VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, @@ -72,8 +69,11 @@ vkdevice::vkdevice(const window& window) }, } { load_vulkan(); + create_instance(); + create_surface(window); + #ifdef _DEBUG create_debug_layer_callback(); #endif // _DEBUG @@ -82,9 +82,58 @@ vkdevice::vkdevice(const window& window) create_memory_allocator(); - create_surface(window); - create_swapchain(); + + bindless = bindless_model::create_bindless_model(device); +} + +vkdevice* vkdevice::init_device(const window& window) { + vkdevice::render_device = new vkdevice(window); + return render_device; +} + +void vkdevice::free_device() { + delete render_device; + render_device = nullptr; +} + +vkdevice::~vkdevice() { + vkDeviceWaitIdle(device); + + bindless_model::destroy_bindless_model(device, bindless); + + for (auto& texture : textures) { + destroy_texture(texture); + } + + for (auto& buffer : buffers) { + destroy_buffer(buffer); + } + + for (auto& pipeline : pipelines) { + destroy_pipeline(pipeline); + } + + if (swapchain != nullptr) { + vkDestroySwapchainKHR(device, swapchain, nullptr); + } + + vmaDestroyAllocator(gpu_allocator); + + for (auto& queue : queues) { + vkDestroyCommandPool(device, queue.command_pool, nullptr); + } + + vkDestroyDevice(device, nullptr); + +#ifdef _DEBUG + vkDestroyDebugUtilsMessengerEXT(instance, debug_messenger, nullptr); +#endif // _DEBUG + + if (surface != nullptr) { + vkDestroySurfaceKHR(instance, surface, nullptr); + } + vkDestroyInstance(instance, nullptr); } handle vkdevice::create_texture(const texture_desc& desc) { @@ -126,13 +175,54 @@ handle vkdevice::create_texture(const texture_desc& desc) { .pUserData = nullptr, }; - VKCHECK(vmaCreateImage(gpu_allocator, &create_info, &alloc_create_info, &device_texture.handle, &device_texture.alloc, nullptr)) - - if ((desc.usages & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0U || - (desc.usages & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) { - create_views(device_texture); + VKCHECK(vmaCreateImage(gpu_allocator, &create_info, &alloc_create_info, &device_texture.vk_image, &device_texture.alloc, nullptr)); + + create_views(device_texture); + + // TODO: Handle mips views + std::vector images_info; + images_info.push_back({ + .sampler = nullptr, + .imageView = device_texture.whole_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }); + + std::vector write_descriptor_sets; + if ((desc.usages & VK_IMAGE_USAGE_STORAGE_BIT) != 0) { + device_texture.storage_index = storage_indices.add(&device_texture); + + write_descriptor_sets.push_back({ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = bindless.sets[BindlessSetType::GLOBAL], + .dstBinding = 2U, + .dstArrayElement = device_texture.storage_index, + .descriptorCount = static_cast(images_info.size()), + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = images_info.data(), + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }); + } + if ((desc.usages & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) { + device_texture.sampled_index = sampled_indices.add(&device_texture); + + write_descriptor_sets.push_back({ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = bindless.sets[BindlessSetType::GLOBAL], + .dstBinding = 1U, + .dstArrayElement = device_texture.sampled_index, + .descriptorCount = static_cast(images_info.size()), + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .pImageInfo = images_info.data(), + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }); } + vkUpdateDescriptorSets(device, write_descriptor_sets.size(), write_descriptor_sets.data(), 0U, nullptr); + return { textures.add(device_texture) }; } @@ -168,21 +258,118 @@ handle vkdevice::create_buffer(const buffer_desc& desc) { VmaAllocationInfo alloc_info {}; - VKCHECK(vmaCreateBuffer(gpu_allocator, &create_info, &alloc_create_info, &device_buffer.handle, &device_buffer.alloc, &alloc_info)) + VKCHECK(vmaCreateBuffer(gpu_allocator, &create_info, &alloc_create_info, &device_buffer.vk_buffer, &device_buffer.alloc, &alloc_info)); device_buffer.mapped_ptr = alloc_info.pMappedData; return { buffers.add(device_buffer) }; } +handle vkdevice::create_pipeline(const pipeline_desc& desc) { + device_pipeline device_pipeline { + .desc = desc, + .bindless = &bindless, + }; + if (!desc.cs_code.empty()) { + const auto& code = desc.cs_code; + VkShaderModule shader_module; + VkShaderModuleCreateInfo module_create_info { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .codeSize = code.size(), + .pCode = (uint32_t*)code.data(), + }; + + VKCHECK(vkCreateShaderModule(device, &module_create_info, nullptr, &shader_module)); + + VkPipelineShaderStageCreateInfo stage_create_info { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = shader_module, + .pName = "main", + .pSpecializationInfo = nullptr, + }; + + VkComputePipelineCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .stage = stage_create_info, + .layout = bindless.layout, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + + device_pipeline.bind_point = VK_PIPELINE_BIND_POINT_COMPUTE; + VKCHECK(vkCreateComputePipelines(device, nullptr, 1U, &create_info, nullptr, &device_pipeline.vk_pipeline)); + + vkDestroyShaderModule(device, shader_module, nullptr); + } else { + + VkGraphicsPipelineCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0U, + .layout = bindless.layout, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + + device_pipeline.bind_point = VK_PIPELINE_BIND_POINT_GRAPHICS; + VKCHECK(vkCreateGraphicsPipelines(device, nullptr, 1U, &create_info, nullptr, &device_pipeline.vk_pipeline)); + } + + return { pipelines.add(device_pipeline) }; +} + +void vkdevice::destroy_texture(const device_texture& texture) { + destroy_texture({ .id = textures.find(texture) }); +} + +void vkdevice::destroy_texture(handle handle) { + const auto& texture = get_texture(handle); + textures.remove(handle.id); + + vmaDestroyImage(gpu_allocator, texture.vk_image, texture.alloc); + + vkDestroyImageView(device, texture.whole_view, nullptr); + + for (const auto& mip_view : texture.mips_views) { + vkDestroyImageView(device, mip_view, nullptr); + } +} + +void vkdevice::destroy_buffer(const device_buffer& buffer) { + destroy_buffer({ .id = buffers.find(buffer) }); +} + +void vkdevice::destroy_buffer(handle handle) { + const auto& buffer = get_buffer(handle); + + vmaDestroyBuffer(gpu_allocator, buffer.vk_buffer, buffer.alloc); +} + +void vkdevice::destroy_pipeline(const device_pipeline& pipeline) { + destroy_pipeline({ .id = pipelines.find(pipeline) }); +} + +void vkdevice::destroy_pipeline(handle handle) { + const auto& pipeline = vkdevice::get_pipeline(handle); + + vkDestroyPipeline(device, pipeline.vk_pipeline, nullptr); +} + void vkdevice::submit(command_buffer* buffers, size_t count) { - assert((count - max_submitable_command_buffers) <= 0); + assert((max_submitable_command_buffers - count) >= 0); const auto queue_type = buffers[0].queue_type; VkCommandBuffer to_submit[max_submitable_command_buffers]; for (auto index { 0U }; index < count; index++) { assert(queue_type == buffers[index].queue_type); - to_submit[index] = buffers[index].handle; + to_submit[index] = buffers[index].vk_command_buffer; } VkSubmitInfo submit_info { @@ -197,13 +384,22 @@ void vkdevice::submit(command_buffer* buffers, size_t count) { .pSignalSemaphores = nullptr, }; - VKCHECK(vkQueueSubmit(queues[static_cast(queue_type)].handle, 1, &submit_info, nullptr)) + VKCHECK(vkQueueSubmit(queues[static_cast(queue_type)].vk_queue, 1, &submit_info, nullptr)); +} + +void vkdevice::present() { + VkPresentInfoKHR present_info { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + }; + + // VKCHECK(vkQueuePresentKHR(queues[static_cast(QueueType::GRAPHICS)].vk_queue, &present_info)); } // private void vkdevice::allocate_command_buffers(command_buffer* buffers, size_t count, QueueType type) { - assert((count - max_allocable_command_buffers) <= 0); + assert((max_allocable_command_buffers - count) >= 0); const auto& queue = queues[static_cast(type)]; VkCommandBuffer command_buffers[max_allocable_command_buffers]; @@ -215,22 +411,23 @@ void vkdevice::allocate_command_buffers(command_buffer* buffers, size_t count, Q .commandBufferCount = static_cast(count), }; - VKCHECK(vkAllocateCommandBuffers(device, &allocate_info, command_buffers)) + VKCHECK(vkAllocateCommandBuffers(device, &allocate_info, command_buffers)); for (auto index { 0U }; index < count; index++) { - buffers[index].handle = command_buffers[index]; + buffers[index].vk_command_buffer = command_buffers[index]; + buffers[index].queue_type = type; } } void vkdevice::create_views(device_texture& texture) { - VkImageAspectFlags aspect_mask = ((texture.desc.usages & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0U) - ? VK_IMAGE_ASPECT_COLOR_BIT - : VK_IMAGE_ASPECT_DEPTH_BIT; + VkImageAspectFlags aspect_mask = ((texture.desc.usages & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) + ? VK_IMAGE_ASPECT_DEPTH_BIT + : VK_IMAGE_ASPECT_COLOR_BIT; VkImageViewCreateInfo create_info { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0U, - .image = texture.handle, + .image = texture.vk_image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = texture.desc.format, .components = { @@ -240,7 +437,7 @@ void vkdevice::create_views(device_texture& texture) { .a = VK_COMPONENT_SWIZZLE_A, }, .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, // TODO: Choose either color or depth based on format ? + .aspectMask = aspect_mask, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, @@ -250,14 +447,15 @@ void vkdevice::create_views(device_texture& texture) { for (auto mip_index{ 0 }; mip_index < texture.desc.mips; mip_index++) { create_info.subresourceRange.baseMipLevel = mip_index; - VKCHECK(vkCreateImageView(device, &create_info, nullptr, &texture.mips_views[mip_index])) + VKCHECK(vkCreateImageView(device, &create_info, nullptr, &texture.mips_views[mip_index])); } create_info.subresourceRange.baseMipLevel = 0; create_info.subresourceRange.levelCount = texture.desc.mips; - VKCHECK(vkCreateImageView(device, &create_info, nullptr, &texture.whole_view)) + VKCHECK(vkCreateImageView(device, &create_info, nullptr, &texture.whole_view)); } +// Presentation stuff void vkdevice::create_surface(const window& window) { VkWin32SurfaceCreateInfoKHR create_info { .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, @@ -267,20 +465,18 @@ void vkdevice::create_surface(const window& window) { .hwnd = window.handle, }; - VKCHECK(vkCreateWin32SurfaceKHR(instance, &create_info, nullptr, &surface)) + VKCHECK(vkCreateWin32SurfaceKHR(instance, &create_info, nullptr, &surface)); } void vkdevice::create_swapchain() { VkBool32 supported = VK_FALSE; - for (auto index{ 0U }; index < static_cast(QueueType::MAX); index++) { - auto& queue = queues[index]; - VKCHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue.index, surface, &supported)) + auto& queue = queues[static_cast(QueueType::GRAPHICS)]; + VKCHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue.index, surface, &supported)); - assert(supported); - } + assert(supported); VkSurfaceCapabilitiesKHR surface_capabilities = {}; - VKCHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_capabilities)) + VKCHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_capabilities)); VkExtent2D extent = surface_capabilities.currentExtent; @@ -297,25 +493,27 @@ void vkdevice::create_swapchain() { } auto supported_formats_count = 0U; - VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &supported_formats_count, VK_NULL_HANDLE)) + VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &supported_formats_count, nullptr)); std::vector supported_formats{ supported_formats_count }; - VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &supported_formats_count, supported_formats.data())) + VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &supported_formats_count, supported_formats.data())); auto supported_present_modes_count = 0U; - VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &supported_present_modes_count, VK_NULL_HANDLE)) + VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &supported_present_modes_count, nullptr)); std::vector supported_present_modes{ supported_present_modes_count }; - VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &supported_present_modes_count, supported_present_modes.data())) + VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &supported_present_modes_count, supported_present_modes.data())); + + auto min_image_count = (surface_capabilities.minImageCount <= virtual_frames_count) ? virtual_frames_count : surface_capabilities.minImageCount; VkSwapchainCreateInfoKHR create_info = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = nullptr, .flags = 0U, .surface = surface, - .minImageCount = surface_capabilities.minImageCount, + .minImageCount = min_image_count, .imageFormat = supported_formats[0].format, .imageColorSpace = supported_formats[0].colorSpace, .imageExtent = extent, @@ -331,7 +529,28 @@ void vkdevice::create_swapchain() { .oldSwapchain = nullptr, }; - VKCHECK(vkCreateSwapchainKHR(device, &create_info, nullptr, &swapchain)) + VKCHECK(vkCreateSwapchainKHR(device, &create_info, nullptr, &swapchain)); +} + +void vkdevice::create_virtual_frames() { + for (auto index { 0U }; index < virtual_frames_count; index++) { + { + VkSemaphoreTypeCreateInfo semaphore_type_create_info { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + .pNext = nullptr, + .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, + .initialValue = 0U, + }; + + VkSemaphoreCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &semaphore_type_create_info, + .flags = 0U, + }; + + VKCHECK(vkCreateSemaphore(device, &create_info, nullptr, &semaphores[index])); + } + } } // ----------------- vkadapter ----------------- @@ -367,7 +586,7 @@ void vkdevice::create_instance() { .ppEnabledExtensionNames = instance_extensions, }; - VKCHECK(vkCreateInstance(&create_info, nullptr, &instance)) + VKCHECK(vkCreateInstance(&create_info, nullptr, &instance)); load_instance_functions(instance); } @@ -376,35 +595,49 @@ void vkdevice::create_device() { pick_physical_device(); float queue_priority = 1.f; - VkDeviceQueueCreateInfo queue_create_infos[static_cast(QueueType::MAX)] { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - nullptr, - 0U, - 0, - 1, - &queue_priority, - }; + VkDeviceQueueCreateInfo queue_create_infos[static_cast(QueueType::MAX)]; + + for (auto& queue_create_info : queue_create_infos) { + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.pNext = nullptr; + queue_create_info.flags = 0U; + queue_create_info.queueCount = 1; + queue_create_info.pQueuePriorities = &queue_priority; + } + + auto queue_properties_count = 0U; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, nullptr); + + std::vector queue_properties { queue_properties_count }; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, queue_properties.data()); for (auto index{ 0U }; index < static_cast(QueueType::MAX); index++) { auto& queue = queues[index]; - auto queue_index = pick_queue(queue.usages); - queue.index = queue_index; + for (auto properties_index { 0U }; properties_index < queue_properties_count; properties_index++) { + auto& queue_property = queue_properties[properties_index]; + + if ((queue_property.queueFlags & queue.usages) == queue.usages) { + queue_create_infos[index].queueFamilyIndex = queue.index = properties_index; + queue_property.queueFlags = 0U; + } + } } constexpr const char* device_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, + // VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME }; VkPhysicalDeviceVulkan12Features physical_device_12_features { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, - .pNext = VK_NULL_HANDLE, + .pNext = nullptr, .shaderSampledImageArrayNonUniformIndexing = VK_TRUE, .shaderStorageImageArrayNonUniformIndexing = VK_TRUE, .descriptorBindingUpdateUnusedWhilePending = VK_TRUE, .descriptorBindingPartiallyBound = VK_TRUE, .runtimeDescriptorArray = VK_TRUE, - .imagelessFramebuffer = VK_TRUE, + .timelineSemaphore = VK_TRUE, .bufferDeviceAddress = VK_TRUE, }; @@ -428,12 +661,12 @@ void vkdevice::create_device() { .pEnabledFeatures = nullptr, }; - VKCHECK(vkCreateDevice(physical_device, &create_info, nullptr, &device)) + VKCHECK(vkCreateDevice(physical_device, &create_info, nullptr, &device)); load_device_functions(device); for (auto& queue : queues) { - vkGetDeviceQueue(device, queue.index, 0, &queue.handle); + vkGetDeviceQueue(device, queue.index, 0, &queue.vk_queue); } create_command_pools(); @@ -448,7 +681,7 @@ void vkdevice::create_memory_allocator() { .vulkanApiVersion = VK_API_VERSION_1_2, }; - VKCHECK(vmaCreateAllocator(&create_info, &gpu_allocator)) + VKCHECK(vmaCreateAllocator(&create_info, &gpu_allocator)); } void vkdevice::create_command_pools() { @@ -461,27 +694,10 @@ void vkdevice::create_command_pools() { for (auto& queue : queues) { create_info.queueFamilyIndex = queue.index; - VKCHECK(vkCreateCommandPool(device, &create_info, nullptr, &queue.command_pool)) + VKCHECK(vkCreateCommandPool(device, &create_info, nullptr, &queue.command_pool)); } } -uint32_t vkdevice::pick_queue(VkQueueFlags queue_usages) const { - auto queue_properties_count = 0U; - vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, nullptr); - - std::vector queue_properties { queue_properties_count }; - vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, queue_properties.data()); - - for (auto properties_index{ 0U }; properties_index < queue_properties_count; properties_index++) { - if ((queue_properties[properties_index].queueFlags & queue_usages) == queue_usages) { - return properties_index; - } - } - - assert(true); - return UINT_MAX; -} - void vkdevice::pick_physical_device() { auto physical_devices_count = 0U; vkEnumeratePhysicalDevices(instance, &physical_devices_count, nullptr); @@ -521,7 +737,6 @@ bool vkdevice::device_support_features(VkPhysicalDevice physical_dev) { vulkan_12_features.shaderStorageImageArrayNonUniformIndexing == VK_FALSE || vulkan_12_features.shaderSampledImageArrayNonUniformIndexing == VK_FALSE || vulkan_12_features.descriptorBindingPartiallyBound == VK_FALSE || - vulkan_12_features.descriptorBindingPartiallyBound == VK_FALSE || vulkan_12_features.descriptorBindingUpdateUnusedWhilePending == VK_FALSE || vulkan_12_features.imagelessFramebuffer == VK_FALSE) { return false; @@ -532,7 +747,7 @@ bool vkdevice::device_support_features(VkPhysicalDevice physical_dev) { // Vulkan 1.3 physical device features support { VkPhysicalDeviceVulkan13Features vulkan_13_features { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, }; VkPhysicalDeviceFeatures2 physical_device_features { @@ -565,5 +780,5 @@ void vkdevice::create_debug_layer_callback() { .pUserData = nullptr, }; - VKCHECK(vkCreateDebugUtilsMessengerEXT(instance, &create_info, nullptr, &debug_messenger)) + VKCHECK(vkCreateDebugUtilsMessengerEXT(instance, &create_info, nullptr, &debug_messenger)); } diff --git a/include/api-test/buffer.hpp b/include/api-test/buffer.hpp index 500e15b..64c4881 100644 --- a/include/api-test/buffer.hpp +++ b/include/api-test/buffer.hpp @@ -6,13 +6,6 @@ #include "handle.hpp" -struct buffer_desc { - size_t size; - VkBufferUsageFlags usages; - VkMemoryPropertyFlags memory_properties; - uint32_t memory_usage; -}; - struct device_buffer; class buffer { diff --git a/include/api-test/command-buffer.hpp b/include/api-test/command-buffer.hpp index 140a291..1a0e553 100644 --- a/include/api-test/command-buffer.hpp +++ b/include/api-test/command-buffer.hpp @@ -6,6 +6,8 @@ #include "handle.hpp" +#include "vec3.hpp" + enum class QueueType : uint32_t { GRAPHICS, ASYNC_COMPUTE, @@ -14,31 +16,36 @@ enum class QueueType : uint32_t { }; -struct device_buffer; struct device_texture; +struct device_buffer; +struct device_pipeline; + +struct dispatch_params { + handle pipeline; + vec3u group_size; + vec3u local_group_size; +}; struct command_buffer { - void start() const; - void stop() const; + void start() const; + void stop() const; + + void barrier(handle texture_handle, VkPipelineStageFlags2 stage, VkAccessFlags2 access, VkImageLayout layout) const; - void barrier(); + void copy(handle buffer_handle, handle texture_handle, VkDeviceSize offset = 0) const; - VkCommandBuffer handle; + VkCommandBuffer vk_command_buffer; QueueType queue_type; }; -struct graphics_command_buffer : public command_buffer { - void set_pipeline(); - - void dispatch(); +// General purpose queue +struct graphics_command_buffer: public command_buffer { + void dispatch(const dispatch_params& params); }; -struct compute_command_buffer : public command_buffer { - void set_pipeline(); - - void dispatch(); +struct compute_command_buffer: public command_buffer { + void dispatch(const dispatch_params& params); }; -struct transfer_command_buffer : public command_buffer { - void copy(::handle buffer_handle, ::handle texture_handle); +struct transfer_command_buffer: public command_buffer { }; diff --git a/include/api-test/freelist.hpp b/include/api-test/freelist.hpp index e5af6f5..9553a15 100644 --- a/include/api-test/freelist.hpp +++ b/include/api-test/freelist.hpp @@ -1,32 +1,57 @@ #include #include -template + +static constexpr uint32_t default_element_count = 1024U; + +template class freelist { public: freelist() - : holes(initial_size), + : free(initial_size), items(initial_size) { - std::iota(holes.begin(), holes.end(), 0); + std::iota(free.rbegin(), free.rend(), 0); } uint32_t add(const T& item) { - const auto id = holes.back(); - holes.pop_back(); + const auto id = free.back(); + free.pop_back(); - items[id] = std::move(item); + items[id] = item; return id; } void remove(uint32_t item_id) { - holes.push_back(item_id); + free.push_back(item_id); } - const T& operator[](uint32_t index) { + T& operator[](uint32_t index) { return items[index]; } + uint32_t find(const T& seeked_item) { + for (auto index { 0U }; index < items.size(); index++) { + const auto& item = items[index]; + if (memcmp(&item, &seeked_item, sizeof(T)) == 0) { + return index; + } + } + + return invalid_handle; + } + + auto begin() { + return items.begin(); + } + + auto end() { + return items.end(); + } + + static constexpr uint32_t invalid_handle = -1; + +private: std::vector items; - std::vector holes; + std::vector free; }; diff --git a/include/api-test/texture.hpp b/include/api-test/texture.hpp index 9632c5e..59bfec1 100644 --- a/include/api-test/texture.hpp +++ b/include/api-test/texture.hpp @@ -6,18 +6,6 @@ #include "handle.hpp" -struct texture_desc { - uint32_t width = 1U; - uint32_t height = 1U; - uint32_t depth = 1U; - uint32_t mips = 1U; - VkImageUsageFlags usages; - VkFormat format; - VkImageType type; - - static constexpr auto max_mips = 16U; -}; - struct device_texture; class texture { diff --git a/include/api-test/vk-bindless.hpp b/include/api-test/vk-bindless.hpp new file mode 100644 index 0000000..d8fac3c --- /dev/null +++ b/include/api-test/vk-bindless.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include + +enum BindlessSetType : uint32_t { + GLOBAL, + MAX, +}; + +enum SamplerType : uint32_t { + NEAREST, + LINEAR, + MAX_SAMPLER, +}; + +struct bindless_model { +#ifndef USE_VK_DESCRIPTOR_BUFFERS + VkPipelineLayout layout; + VkDescriptorSetLayout sets_layout[BindlessSetType::MAX]; + VkDescriptorSet sets[BindlessSetType::MAX]; + VkDescriptorPool descriptor_pool; + + static constexpr size_t descriptor_pool_allocable_sets_count = 64U; + static constexpr uint32_t set_descriptors_count[BindlessSetType::MAX]{ 1024U }; + + static bindless_model create_bindless_model(VkDevice device); + + static void destroy_bindless_model(VkDevice device, bindless_model& bindless); + + private: + void create_layout(VkDevice device); + void create_descriptor_pool(VkDevice device); + void allocate_sets(VkDevice device); + + void destroy_descriptor_pool(VkDevice device); + + void create_immutable_samplers(VkDevice device); + + void destroy_immutable_samplers(VkDevice device); + + VkSampler samplers[SamplerType::MAX_SAMPLER]; +#else + VkPipelineLayout layout; + VkDescriptorSetLayout sets_layout[BindlessSetType::MAX]; + + static constexpr size_t descriptor_pool_allocable_sets_count = 64U; + static constexpr uint32_t set_descriptors_count[BindlessSetType::MAX]{ 1024U }; + + static bindless_model create_bindless_model(VkDevice device); + + static void destroy_bindless_model(VkDevice device, bindless_model& bindless); + + private: + void create_layout(VkDevice device); + + handle storage_image_descriptors; + handle sampled_image_descriptors; +#endif +}; diff --git a/include/api-test/vk-device-types.hpp b/include/api-test/vk-device-types.hpp new file mode 100644 index 0000000..33a7738 --- /dev/null +++ b/include/api-test/vk-device-types.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include + +#include "vk-bindless.hpp" + +using VmaAllocation = struct VmaAllocation_T*; + +struct texture_desc { + uint32_t width = 1U; + uint32_t height = 1U; + uint32_t depth = 1U; + uint32_t mips = 1U; + VkImageUsageFlags usages; + VkFormat format; + VkImageType type; + + static constexpr auto max_mips = 16U; +}; + +struct device_texture { + VkImage vk_image; + VkImageView whole_view; + VkImageView mips_views[texture_desc::max_mips] = { VK_NULL_HANDLE }; + VkImageLayout layout; + VkPipelineStageFlags2 stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags2 access = VK_ACCESS_2_NONE; + + uint32_t storage_index = -1; + uint32_t sampled_index = -1; + uint32_t sampler_index = -1; + + VmaAllocation alloc; + + texture_desc desc; +}; + + +struct buffer_desc { + size_t size; + VkBufferUsageFlags usages; + VkMemoryPropertyFlags memory_properties; + uint32_t memory_usage; +}; + +struct device_buffer { + VkBuffer vk_buffer; + VmaAllocation alloc; + VkDeviceAddress device_address; + void* mapped_ptr; + + buffer_desc desc; +}; + + +struct pipeline_desc { + std::span vs_code; + std::span fs_code; + std::span cs_code; +}; + +struct device_pipeline { + VkPipeline vk_pipeline; + VkPipelineBindPoint bind_point; + pipeline_desc desc; + + bindless_model* bindless = nullptr; +}; diff --git a/include/api-test/vk-device.hpp b/include/api-test/vk-device.hpp index f010cc5..b09ed9e 100644 --- a/include/api-test/vk-device.hpp +++ b/include/api-test/vk-device.hpp @@ -1,115 +1,137 @@ #pragma once #include +#include #include -#include "handle.hpp" -#include "freelist.hpp" -#include "texture.hpp" -#include "buffer.hpp" #include "command-buffer.hpp" +#include "freelist.hpp" +#include "handle.hpp" +#include "vk-bindless.hpp" +#include "vk-device-types.hpp" using VmaAllocator = struct VmaAllocator_T*; -using VmaAllocation = struct VmaAllocation_T*; - -struct device_texture { - VkImage handle; - VkImageView whole_view; - VkImageView mips_views[texture_desc::max_mips] = { VK_NULL_HANDLE }; - VmaAllocation alloc; - - texture_desc desc; -}; - -struct device_buffer { - VkBuffer handle; - VmaAllocation alloc; - VkDeviceAddress device_address; - void* mapped_ptr; - - buffer_desc desc; -}; class window; class vkdevice { struct queue { - VkQueue handle; - uint32_t index; - VkQueueFlags usages; - VkCommandPool command_pool; + VkQueue vk_queue; + uint32_t index; + VkQueueFlags usages; + VkCommandPool command_pool; }; -public: + public: + static vkdevice* init_device(const window& window); - vkdevice(const window& window); + static void free_device(); + + static inline vkdevice* get_device() { + return render_device; + } + + handle create_texture(const texture_desc& desc); + + handle create_buffer(const buffer_desc& desc); - handle create_texture(const texture_desc& desc); + handle create_pipeline(const pipeline_desc& desc); - handle create_buffer(const buffer_desc& desc); + void destroy_texture(const device_texture& texture); + void destroy_texture(handle handle); + + void destroy_buffer(const device_buffer& buffer); + void destroy_buffer(handle handle); + + void destroy_pipeline(const device_pipeline& pipeline); + void destroy_pipeline(handle handle); template void allocate_command_buffers(cmd_buf_type* buffers, size_t count, QueueType queue_type) { - allocate_command_buffers(buffers, count, queue_type); + allocate_command_buffers(static_cast(buffers), count, queue_type); } - const device_texture& get_texture(handle handle) { + inline device_texture& get_texture(handle handle) { return textures[handle.id]; } - const device_buffer& get_buffer(handle handle) { + inline const device_buffer& get_buffer(handle handle) { return buffers[handle.id]; } + inline const device_pipeline& get_pipeline(handle handle) { + return pipelines[handle.id]; + } + + inline const bindless_model& get_bindingmodel() { + return bindless; + } + void submit(command_buffer* buffers, size_t count); -private: + void present(); + + private: + vkdevice(const window& window); + ~vkdevice(); void allocate_command_buffers(command_buffer* buffers, size_t count, QueueType type); void create_views(device_texture& texture); - void create_surface(const window& window); + // Presentation stuff + void create_surface(const window& window); - void create_swapchain(); + void create_swapchain(); - VkSurfaceKHR surface; - VkSwapchainKHR swapchain; + void create_virtual_frames(); - // TODO: Wrap in a struct called adapter ? - void create_instance(); + VkSurfaceKHR surface = nullptr; + VkSwapchainKHR swapchain = nullptr; - void create_device(); + // TODO: Wrap in a struct called adapter ? + void create_instance(); - void create_memory_allocator(); + void create_device(); - void create_command_pools(); + void create_memory_allocator(); - void pick_physical_device(); + void create_command_pools(); - [[nodiscard]] uint32_t pick_queue(VkQueueFlags usages) const; + void pick_physical_device(); static bool device_support_features(VkPhysicalDevice physical_dev); - void create_debug_layer_callback(); + void create_debug_layer_callback(); + + // Virtual frames resources + static constexpr uint32_t virtual_frames_count = 3U; + VkSemaphore semaphores[virtual_frames_count]; + + queue queues[static_cast(QueueType::MAX)]; + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; - queue queues[static_cast(QueueType::MAX)]; + VmaAllocator gpu_allocator; - VkInstance instance; - VkPhysicalDevice physical_device; - VkDevice device; + bindless_model bindless; - VmaAllocator gpu_allocator; + static constexpr size_t max_allocable_command_buffers = 16; + static constexpr size_t max_submitable_command_buffers = 16; - static constexpr size_t max_allocable_command_buffers = 16; - static constexpr size_t max_submitable_command_buffers = 16; + freelist storage_indices; + freelist sampled_indices; + + freelist textures; + freelist buffers; + freelist pipelines; + + static vkdevice* render_device; #ifdef _DEBUG - VkDebugUtilsMessengerEXT debug_messenger; + VkDebugUtilsMessengerEXT debug_messenger; #endif // _DEBUG - - freelist textures; - freelist buffers; }; diff --git a/include/api-test/vk-utils.hpp b/include/api-test/vk-utils.hpp new file mode 100644 index 0000000..fb02737 --- /dev/null +++ b/include/api-test/vk-utils.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +#ifdef _DEBUG +#define VKCHECK(result) assert((result) == VK_SUCCESS) +#else +#define VKCHECK(result) (result) +#endif diff --git a/include/vec3.hpp b/include/vec3.hpp index 2fec69e..75b67d2 100644 --- a/include/vec3.hpp +++ b/include/vec3.hpp @@ -2,6 +2,14 @@ #define __VEC3_HPP_ #include +#include + +struct vec3u { + union { + uint32_t vec[3]; + uint32_t x, y, z; + }; +}; class vec3 { public: @@ -24,6 +32,10 @@ class vec3 { vec3& operator/=(float numerator); + vec3 operator<(const vec3& vec) const; + + vec3 operator>(const vec3& vec) const; + float operator[](int axis); [[nodiscard]] float length() const; @@ -34,10 +46,6 @@ class vec3 { [[nodiscard]] vec3 cross(const vec3& vec) const; - [[nodiscard]] vec3 min(const vec3& vec) const; - - [[nodiscard]] vec3 max(const vec3& vec) const; - vec3& normalize(); [[nodiscard]] bool near_zero() const; diff --git a/include/vulkan-loader.hpp b/include/vulkan-loader.hpp index e3e2cd4..d0ad3a5 100644 --- a/include/vulkan-loader.hpp +++ b/include/vulkan-loader.hpp @@ -84,6 +84,7 @@ typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES; X(vkDestroyCommandPool) \ X(vkBeginCommandBuffer) \ X(vkCmdPipelineBarrier) \ + X(vkCmdPipelineBarrier2) \ X(vkCmdPushConstants) \ X(vkCmdClearColorImage) \ X(vkCmdBeginRenderPass) \ diff --git a/shaders/CMakeLists.txt b/shaders/CMakeLists.txt index 699e921..f572ced 100644 --- a/shaders/CMakeLists.txt +++ b/shaders/CMakeLists.txt @@ -21,6 +21,7 @@ endforeach(GLSL) add_custom_target( shaders DEPENDS ${SPIRV_BINARY_FILES} - ) +) add_dependencies(path-tracer shaders) +add_dependencies(api-test shaders) diff --git a/shaders/test.comp b/shaders/test.comp new file mode 100644 index 0000000..541cb93 --- /dev/null +++ b/shaders/test.comp @@ -0,0 +1,18 @@ +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_nonuniform_qualifier : require + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(set = 0, binding = 0) uniform sampler samplers[]; +layout(set = 0, binding = 1) uniform texture2D textures[]; +layout(set = 0, binding = 2, rgba32f) uniform image2D images[]; + +void main() { + ivec2 coords = ivec2(gl_GlobalInvocationID.xy); + float t = 0.5; + vec3 color_1 = imageLoad(images[nonuniformEXT(0)], coords).xyz; + vec3 color_2 = imageLoad(images[nonuniformEXT(1)], coords).xyz; + vec3 out_color = (t * color_1) + ((1.0 - t) * color_2); + + imageStore(images[nonuniformEXT(0)], coords, vec4(out_color, 1.0)); +} -- cgit v1.3