aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQuentin LADEVIE <qladevie@wanadev.fr>2023-01-22 17:59:24 +0100
committerQuentin LADEVIE <qladevie@wanadev.fr>2023-01-22 17:59:24 +0100
commit0a882c4b85423a170455f05a52d18f7fa7c9220b (patch)
tree3bdc24d73ba7cc828fce2b93ce85f73831b12204
parent5e3bb454d50091e581d5d1d03af34e38277694b3 (diff)
Api test bindless and command buffers
-rw-r--r--api-proto.txt7
-rw-r--r--api-test/CMakeLists.txt1
-rw-r--r--api-test/command-buffer.cpp123
-rw-r--r--api-test/main.cpp101
-rw-r--r--api-test/vk-bindless.cpp218
-rw-r--r--api-test/vk-device.cpp365
-rw-r--r--include/api-test/buffer.hpp7
-rw-r--r--include/api-test/command-buffer.hpp37
-rw-r--r--include/api-test/freelist.hpp43
-rw-r--r--include/api-test/texture.hpp12
-rw-r--r--include/api-test/vk-bindless.hpp61
-rw-r--r--include/api-test/vk-device-types.hpp70
-rw-r--r--include/api-test/vk-device.hpp138
-rw-r--r--include/api-test/vk-utils.hpp11
-rw-r--r--include/vec3.hpp16
-rw-r--r--include/vulkan-loader.hpp1
-rw-r--r--shaders/CMakeLists.txt3
-rw-r--r--shaders/test.comp18
18 files changed, 995 insertions, 237 deletions
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<device_command_buffer> cmd_buffers, CMD_BUF_TYPE type);
+ submit(span<device_command_buffer> 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 <cassert>
+#include <vulkan/vulkan_core.h>
#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<device_buffer> buffer_handle, ::handle<device_texture> 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<device_texture> 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<device_buffer> buffer_handle, ::handle<device_texture> 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 <cassert>
#include <cstdio>
+#include <span>
#include <vk_mem_alloc.h>
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb_image.h>
+
#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);
+
+ 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<size_t>(width) * height * 4U;
+ memcpy(staging_buffer.mapped_ptr, data, size);
- auto gpu_texture_handle = device.create_texture({
- .width = 1024U,
- .height = 1024U,
- .usages = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ auto gpu_texture_handle = device->create_texture({
+ .width = static_cast<uint32_t>(width),
+ .height = static_cast<uint32_t>(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<size_t>(width) * height * 4U;
+ auto* addr = static_cast<void*>(static_cast<uint8_t*>(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<uint32_t>(width),
+ .height = static_cast<uint32_t>(height),
+ .usages = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
+ .format = VK_FORMAT_R8G8B8A8_UNORM,
+ .type = VK_IMAGE_TYPE_2D,
+ });
+
+ auto output_texture_handle = device->create_texture({
+ .width = static_cast<uint32_t>(width),
+ .height = static_cast<uint32_t>(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),
});
- auto staging_buffer = device.get_buffer(staging_buffer_handle);
- uint64_t t = 7;
- (*(uint64_t*)staging_buffer.mapped_ptr) = t;
- 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<VkDeviceSize>(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<device_texture> vkdevice::create_texture(const texture_desc& desc) {
@@ -126,12 +175,53 @@ handle<device_texture> 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))
+ 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<VkDescriptorImageInfo> images_info;
+ images_info.push_back({
+ .sampler = nullptr,
+ .imageView = device_texture.whole_view,
+ .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
+ });
+
+ std::vector<VkWriteDescriptorSet> write_descriptor_sets;
+ if ((desc.usages & VK_IMAGE_USAGE_STORAGE_BIT) != 0) {
+ device_texture.storage_index = storage_indices.add(&device_texture);
- if ((desc.usages & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0U ||
- (desc.usages & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) {
- create_views(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<uint32_t>(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<uint32_t>(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<device_buffer> 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<device_pipeline> 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<device_texture> 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<device_buffer> 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<device_pipeline> 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<uint32_t>(queue_type)].handle, 1, &submit_info, nullptr))
+ VKCHECK(vkQueueSubmit(queues[static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(QueueType::MAX); index++) {
- auto& queue = queues[index];
- VKCHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue.index, surface, &supported))
+ auto& queue = queues[static_cast<uint32_t>(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<VkSurfaceFormatKHR> 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<VkPresentModeKHR> 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<uint32_t>(QueueType::MAX)] {
- VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
- nullptr,
- 0U,
- 0,
- 1,
- &queue_priority,
- };
+ VkDeviceQueueCreateInfo queue_create_infos[static_cast<uint32_t>(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<VkQueueFamilyProperties> queue_properties { queue_properties_count };
+ vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, queue_properties.data());
for (auto index{ 0U }; index < static_cast<uint32_t>(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,25 +694,8 @@ void vkdevice::create_command_pools() {
for (auto& queue : queues) {
create_info.queueFamilyIndex = queue.index;
- 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<VkQueueFamilyProperties> 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;
- }
+ VKCHECK(vkCreateCommandPool(device, &create_info, nullptr, &queue.command_pool));
}
-
- assert(true);
- return UINT_MAX;
}
void vkdevice::pick_physical_device() {
@@ -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<device_pipeline> 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<device_texture> texture_handle, VkPipelineStageFlags2 stage, VkAccessFlags2 access, VkImageLayout layout) const;
- void barrier();
+ void copy(handle<device_buffer> buffer_handle, handle<device_texture> 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<device_buffer> buffer_handle, ::handle<device_texture> 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 <numeric>
#include <vector>
-template<typename T, const size_t initial_size = 1024U>
+
+static constexpr uint32_t default_element_count = 1024U;
+
+template<typename T, const size_t initial_size = default_element_count>
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<T> items;
- std::vector<uint32_t> holes;
+ std::vector<uint32_t> 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 <cstdint>
+
+#include <vulkan/vulkan_core.h>
+
+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<device_buffer> storage_image_descriptors;
+ handle<device_buffer> 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 <vulkan/vulkan_core.h>
+
+#include <span>
+
+#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<uint8_t> vs_code;
+ std::span<uint8_t> fs_code;
+ std::span<uint8_t> 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 <cstdint>
+#include <span>
#include <vulkan/vulkan_core.h>
-#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<device_texture> create_texture(const texture_desc& desc);
+
+ handle<device_buffer> create_buffer(const buffer_desc& desc);
- handle<device_texture> create_texture(const texture_desc& desc);
+ handle<device_pipeline> create_pipeline(const pipeline_desc& desc);
- handle<device_buffer> create_buffer(const buffer_desc& desc);
+ void destroy_texture(const device_texture& texture);
+ void destroy_texture(handle<device_texture> handle);
+
+ void destroy_buffer(const device_buffer& buffer);
+ void destroy_buffer(handle<device_buffer> handle);
+
+ void destroy_pipeline(const device_pipeline& pipeline);
+ void destroy_pipeline(handle<device_pipeline> handle);
template<typename cmd_buf_type>
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<command_buffer*>(buffers), count, queue_type);
}
- const device_texture& get_texture(handle<device_texture> handle) {
+ inline device_texture& get_texture(handle<device_texture> handle) {
return textures[handle.id];
}
- const device_buffer& get_buffer(handle<device_buffer> handle) {
+ inline const device_buffer& get_buffer(handle<device_buffer> handle) {
return buffers[handle.id];
}
+ inline const device_pipeline& get_pipeline(handle<device_pipeline> 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<uint32_t>(QueueType::MAX)];
+ VkInstance instance;
+ VkPhysicalDevice physical_device;
+ VkDevice device;
- queue queues[static_cast<uint32_t>(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<device_texture*> storage_indices;
+ freelist<device_texture*> sampled_indices;
+
+ freelist<device_texture> textures;
+ freelist<device_buffer> buffers;
+ freelist<device_pipeline> pipelines;
+
+ static vkdevice* render_device;
#ifdef _DEBUG
- VkDebugUtilsMessengerEXT debug_messenger;
+ VkDebugUtilsMessengerEXT debug_messenger;
#endif // _DEBUG
-
- freelist<device_texture> textures;
- freelist<device_buffer> 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 <cassert>
+
+#include <vulkan/vulkan_core.h>
+
+#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 <xmmintrin.h>
+#include <cstdint>
+
+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));
+}