From 4e695770f3264e98055ab3bdc7cfd1a9672090b7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 15 Sep 2017 18:43:32 -0700 Subject: [PATCH] implement vulkan fences and command pools; add more validation to swapchain creation --- src/vulkan/api_objects.cpp | 147 +++++++++++++++++++++++++++++++++- src/vulkan/api_objects.h | 129 +++++++++++++++++++++++++++++ src/vulkan_icd/vulkan_icd.cpp | 127 ++++++++++++++++++++--------- src/vulkan_icd/x11_wsi.cpp | 49 +++++++++++- 4 files changed, 413 insertions(+), 39 deletions(-) diff --git a/src/vulkan/api_objects.cpp b/src/vulkan/api_objects.cpp index 199f7da..6591b3d 100644 --- a/src/vulkan/api_objects.cpp +++ b/src/vulkan/api_objects.cpp @@ -23,6 +23,9 @@ #include "api_objects.h" #include "util/optional.h" #include +#include +#include +#include namespace kazan { @@ -366,10 +369,152 @@ std::unique_ptr Vulkan_semaphore::create(Vulkan_device &device return std::make_unique(); } -std::unique_ptr Vulkan_image::create(Vulkan_device &device, const VkImageCreateInfo &create_info) +VkResult Vulkan_fence::wait_multiple(std::uint32_t fence_count, + const VkFence *fences, + bool wait_for_all, + std::uint64_t timeout) +{ + if(fence_count == 0) + return VK_SUCCESS; + assert(fences); + + typedef std::chrono::steady_clock::duration Duration; + typedef std::chrono::steady_clock::time_point Time_point; + + // assume anything over 1000000 hours is + // infinite; 1000000 hours is about 114 + // years, however, it's still way less than + // 2^63 nanoseconds, so we won't overflow + constexpr std::chrono::hours max_wait_time(1000000); + util::optional wait_duration; // nullopt means infinite timeout + if(timeout <= static_cast( + std::chrono::duration_cast(max_wait_time).count())) + { + wait_duration = std::chrono::duration_cast(std::chrono::nanoseconds(timeout)); + if(wait_duration->count() == 0 && timeout != 0) + wait_duration = Duration(1); // round up so we will sleep some + } + if(wait_duration && wait_duration->count() == 0) + { + bool found = false; + bool search_for = !wait_for_all; + for(std::uint32_t i = 0; i < fence_count; i++) + { + assert(fences[i]); + if(from_handle(fences[i])->is_signaled() == search_for) + { + found = true; + break; + } + } + if(found && wait_for_all) + return VK_TIMEOUT; + if(!found && !wait_for_all) + return VK_TIMEOUT; + return VK_SUCCESS; + } + auto start_time = std::chrono::steady_clock::now(); + util::optional end_time; // nullopt means infinite timeout + if(wait_duration && (start_time.time_since_epoch().count() <= 0 + || Duration::max() - start_time.time_since_epoch() >= *wait_duration)) + end_time = start_time + *wait_duration; + Waiter waiter(wait_for_all ? fence_count : 1); + std::vector::iterator> iters; + iters.reserve(fence_count); + struct Fence_cleanup + { + std::vector::iterator> &iters; + const VkFence *fences; + ~Fence_cleanup() + { + for(std::uint32_t i = 0; i < iters.size(); i++) + { + auto *fence = from_handle(fences[i]); + assert(fence); + std::unique_lock lock_it(fence->lock); + fence->waiters.erase(iters[i]); + } + } + } cleanup = { + .iters = iters, .fences = fences, + }; + for(std::uint32_t i = 0; i < fence_count; i++) + { + auto *fence = from_handle(fences[i]); + assert(fence); + std::unique_lock lock_it(fence->lock); + iters.push_back(fence->waiters.insert(fence->waiters.end(), &waiter)); + if(fence->signaled) + waiter.notify(false); + } + assert(iters.size() == fence_count); + return waiter.wait(end_time) ? VK_SUCCESS : VK_TIMEOUT; +} + +std::unique_ptr Vulkan_fence::create(Vulkan_device &device, + const VkFenceCreateInfo &create_info) +{ + assert(create_info.sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); + assert((create_info.flags & ~VK_FENCE_CREATE_SIGNALED_BIT) == 0); + return std::make_unique(create_info.flags); +} + +std::unique_ptr Vulkan_image::create(Vulkan_device &device, + const VkImageCreateInfo &create_info) { #warning finish implementing Vulkan_image::create return std::make_unique(); } + +Vulkan_command_buffer::Vulkan_command_buffer( + std::list>::iterator iter, + Vulkan_command_pool &command_pool, + Vulkan_device &device) noexcept : iter(iter), + command_pool(command_pool), + device(device) +{ +} + +void Vulkan_command_buffer::reset(VkCommandPoolResetFlags flags) +{ +#warning finish implementing Vulkan_command_buffer::reset +} + +void Vulkan_command_pool::allocate_multiple(Vulkan_device &device, + const VkCommandBufferAllocateInfo &allocate_info, + VkCommandBuffer *allocated_command_buffers) +{ + std::uint32_t command_buffer_count = allocate_info.commandBufferCount; + try + { + std::list> current_command_buffers; + for(std::uint32_t i = 0; i < command_buffer_count; i++) + { + auto iter = current_command_buffers.emplace(current_command_buffers.end()); + auto command_buffer = std::make_unique(iter, *this, device); + allocated_command_buffers[i] = to_handle(command_buffer.get()); + *iter = std::move(command_buffer); + } + command_buffers.splice(command_buffers.end(), current_command_buffers); + } + catch(...) + { + for(std::uint32_t i = 0; i < command_buffer_count; i++) + allocated_command_buffers[i] = VK_NULL_HANDLE; + throw; + } +} + +std::unique_ptr Vulkan_command_pool::create( + Vulkan_device &device, const VkCommandPoolCreateInfo &create_info) +{ + assert(create_info.sType == VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO); + assert(create_info.queueFamilyIndex < Vulkan_physical_device::queue_family_property_count); + assert((create_info.flags + & ~(VK_COMMAND_POOL_CREATE_TRANSIENT_BIT + | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)) + == 0); + return std::make_unique(); +} } } diff --git a/src/vulkan/api_objects.h b/src/vulkan/api_objects.h index 2e8adb4..cf44431 100644 --- a/src/vulkan/api_objects.h +++ b/src/vulkan/api_objects.h @@ -31,10 +31,16 @@ #include "util/variant.h" #include "util/system_memory_info.h" #include "util/constexpr_array.h" +#include "util/optional.h" #include #include #include #include +#include +#include +#include +#include +#include namespace kazan { @@ -1568,6 +1574,84 @@ struct Vulkan_semaphore : public Vulkan_nondispatchable_object +{ +private: + struct Waiter + { + std::mutex lock; + std::condition_variable cond; + std::uint32_t wait_count; + explicit Waiter(std::uint32_t wait_count) : lock(), cond(), wait_count(wait_count) + { + } + void notify(bool notify_condition_variable) + { + std::unique_lock lock_it(lock); + if(wait_count != 0) + { + wait_count--; + if(notify_condition_variable && wait_count == 0) + cond.notify_all(); + } + } + bool wait(util::optional end_time) + { + std::unique_lock lock_it(lock); + while(wait_count != 0) + { + if(end_time) + cond.wait_until(lock_it, *end_time); + else + cond.wait(lock_it); + } + return wait_count == 0; + } + }; + +private: + std::mutex lock; + bool signaled; + std::list waiters; + +public: + explicit Vulkan_fence(VkFenceCreateFlags flags) + : lock(), signaled(flags & VK_FENCE_CREATE_SIGNALED_BIT), waiters() + { + } + bool is_signaled() + { + std::unique_lock lock_it(lock); + return signaled; + } + void set_signaled(bool new_signaled) + { + std::unique_lock lock_it(lock); + if(signaled == new_signaled) + return; + signaled = new_signaled; + if(new_signaled) + { + for(auto *waiter : waiters) + waiter->notify(true); + } + } + void signal() + { + set_signaled(true); + } + void reset() + { + set_signaled(false); + } + static VkResult wait_multiple(std::uint32_t fence_count, + const VkFence *fences, + bool wait_for_all, + std::uint64_t timeout); + static std::unique_ptr create(Vulkan_device &device, + const VkFenceCreateInfo &create_info); +}; + struct Vulkan_image : public Vulkan_nondispatchable_object { virtual ~Vulkan_image() = default; @@ -1575,6 +1659,51 @@ struct Vulkan_image : public Vulkan_nondispatchable_object create(Vulkan_device &device, const VkImageCreateInfo &create_info); }; + +struct Vulkan_command_pool; + +struct Vulkan_command_buffer + : public Vulkan_dispatchable_object +{ + std::list>::iterator iter; + Vulkan_command_pool &command_pool; + Vulkan_device &device; + Vulkan_command_buffer(std::list>::iterator iter, + Vulkan_command_pool &command_pool, + Vulkan_device &device) noexcept; + void reset(VkCommandPoolResetFlags flags); +#warning finish implementing Vulkan_command_buffer +}; + +struct Vulkan_command_pool + : public Vulkan_nondispatchable_object +{ + std::list> command_buffers; + void reset(VkCommandPoolResetFlags flags) + { + for(auto &command_buffer : command_buffers) + command_buffer->reset(flags); + } + void allocate_multiple(Vulkan_device &device, + const VkCommandBufferAllocateInfo &allocate_info, + VkCommandBuffer *allocated_command_buffers); + void free_command_buffer(VkCommandBuffer command_buffer_handle) noexcept + { + if(!command_buffer_handle) + return; + auto *command_buffer = Vulkan_command_buffer::from_handle(command_buffer_handle); + assert(&command_buffer->command_pool == this); + command_buffers.erase(command_buffer->iter); + } + void free_multiple(const VkCommandBuffer *allocated_command_buffers, + std::uint32_t command_buffer_count) noexcept + { + for(std::uint32_t i = 0; i < command_buffer_count; i++) + free_command_buffer(allocated_command_buffers[i]); + } + static std::unique_ptr create(Vulkan_device &device, + const VkCommandPoolCreateInfo &create_info); +}; } } diff --git a/src/vulkan_icd/vulkan_icd.cpp b/src/vulkan_icd/vulkan_icd.cpp index 66ccca7..aa6363b 100644 --- a/src/vulkan_icd/vulkan_icd.cpp +++ b/src/vulkan_icd/vulkan_icd.cpp @@ -396,13 +396,22 @@ extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkQueueBindSparse(VkQueue queue, } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkCreateFence(VkDevice device, - const VkFenceCreateInfo *pCreateInfo, + const VkFenceCreateInfo *create_info, const VkAllocationCallbacks *allocator, - VkFence *pFence) + VkFence *fence) { validate_allocator(allocator); -#warning finish implementing vkCreateFence - assert(!"vkCreateFence is not implemented"); + assert(device); + assert(create_info); + assert(fence); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + auto create_result = vulkan::Vulkan_fence::create( + *vulkan::Vulkan_device::from_handle(device), *create_info); + *fence = move_to_handle(std::move(create_result)); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR void VKAPI_CALL vkDestroyFence(VkDevice device, @@ -410,32 +419,50 @@ extern "C" VKAPI_ATTR void VKAPI_CALL vkDestroyFence(VkDevice device, const VkAllocationCallbacks *allocator) { validate_allocator(allocator); -#warning finish implementing vkDestroyFence - assert(!"vkDestroyFence is not implemented"); + assert(device); + vulkan::Vulkan_fence::move_from_handle(fence).reset(); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkResetFences(VkDevice device, - uint32_t fenceCount, - const VkFence *pFences) + uint32_t fence_count, + const VkFence *fences) { -#warning finish implementing vkResetFences - assert(!"vkResetFences is not implemented"); + assert(device); + assert(fences); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + for(std::uint32_t i = 0; i < fence_count; i++) + vulkan::Vulkan_fence::from_handle(fences[i])->reset(); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus(VkDevice device, VkFence fence) { -#warning finish implementing vkGetFenceStatus - assert(!"vkGetFenceStatus is not implemented"); + assert(device); + assert(fence); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + return vulkan::Vulkan_fence::from_handle(fence)->is_signaled() ? VK_SUCCESS : + VK_NOT_READY; + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkWaitForFences(VkDevice device, - uint32_t fenceCount, - const VkFence *pFences, - VkBool32 waitAll, + uint32_t fence_count, + const VkFence *fences, + VkBool32 wait_all, uint64_t timeout) { -#warning finish implementing vkWaitForFences - assert(!"vkWaitForFences is not implemented"); + assert(device); + assert(fences); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + return vulkan::Vulkan_fence::wait_multiple(fence_count, fences, wait_all, timeout); + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL @@ -883,48 +910,76 @@ extern "C" VKAPI_ATTR void VKAPI_CALL vkGetRenderAreaGranularity(VkDevice device extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool(VkDevice device, - const VkCommandPoolCreateInfo *pCreateInfo, + const VkCommandPoolCreateInfo *create_info, const VkAllocationCallbacks *allocator, - VkCommandPool *pCommandPool) + VkCommandPool *command_pool) { validate_allocator(allocator); -#warning finish implementing vkCreateCommandPool - assert(!"vkCreateCommandPool is not implemented"); + assert(device); + assert(create_info); + assert(command_pool); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + auto create_result = vulkan::Vulkan_command_pool::create( + *vulkan::Vulkan_device::from_handle(device), *create_info); + *command_pool = move_to_handle(std::move(create_result)); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR void VKAPI_CALL vkDestroyCommandPool(VkDevice device, - VkCommandPool commandPool, + VkCommandPool command_pool, const VkAllocationCallbacks *allocator) { validate_allocator(allocator); -#warning finish implementing vkDestroyCommandPool - assert(!"vkDestroyCommandPool is not implemented"); + assert(device); + assert(command_pool); + vulkan::Vulkan_command_pool::move_from_handle(command_pool).reset(); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandPool(VkDevice device, - VkCommandPool commandPool, + VkCommandPool command_pool, VkCommandPoolResetFlags flags) { -#warning finish implementing vkResetCommandPool - assert(!"vkResetCommandPool is not implemented"); + assert(device); + assert(command_pool); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + vulkan::Vulkan_command_pool::from_handle(command_pool)->reset(flags); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers(VkDevice device, - const VkCommandBufferAllocateInfo *pAllocateInfo, - VkCommandBuffer *pCommandBuffers) + const VkCommandBufferAllocateInfo *allocate_info, + VkCommandBuffer *command_buffers) { -#warning finish implementing vkAllocateCommandBuffers - assert(!"vkAllocateCommandBuffers is not implemented"); + assert(device); + assert(allocate_info); + assert(command_buffers); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + vulkan::Vulkan_command_pool::from_handle(allocate_info->commandPool) + ->allocate_multiple( + *vulkan::Vulkan_device::from_handle(device), *allocate_info, command_buffers); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers(VkDevice device, - VkCommandPool commandPool, - uint32_t commandBufferCount, - const VkCommandBuffer *pCommandBuffers) + VkCommandPool command_pool, + uint32_t command_buffer_count, + const VkCommandBuffer *command_buffers) { -#warning finish implementing vkFreeCommandBuffers - assert(!"vkFreeCommandBuffers is not implemented"); + assert(device); + assert(command_pool); + assert(command_buffers); + vulkan::Vulkan_command_pool::from_handle(command_pool) + ->free_multiple(command_buffers, command_buffer_count); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL diff --git a/src/vulkan_icd/x11_wsi.cpp b/src/vulkan_icd/x11_wsi.cpp index 963a7d1..c31f19a 100644 --- a/src/vulkan_icd/x11_wsi.cpp +++ b/src/vulkan_icd/x11_wsi.cpp @@ -398,7 +398,7 @@ struct Xcb_wsi::Implementation VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, }; VkSurfaceCapabilitiesKHR capabilities = { - .minImageCount = 1, + .minImageCount = 2, .maxImageCount = 0, .currentExtent = { @@ -482,6 +482,46 @@ struct Xcb_wsi::Implementation window(window), shm_is_supported(start_setup_results.shm_is_supported) { + assert(create_info.sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); +#warning formats other than VK_FORMAT_B8G8R8A8_UNORM are unimplemented + assert(create_info.imageFormat == VK_FORMAT_B8G8R8A8_UNORM); + assert(create_info.imageColorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); + assert(create_info.imageExtent.width == start_setup_results.image_width); + assert(create_info.imageExtent.height == start_setup_results.image_height); + assert(create_info.imageArrayLayers + <= start_setup_results.capabilities.maxImageArrayLayers); + assert(create_info.imageArrayLayers != 0); + assert((create_info.imageUsage & ~start_setup_results.capabilities.supportedUsageFlags) + == 0); + assert(create_info.preTransform == start_setup_results.capabilities.currentTransform); + assert((create_info.compositeAlpha + & ~start_setup_results.capabilities.supportedCompositeAlpha) + == 0); + const char *warning_message_present_mode_name = nullptr; + switch(create_info.presentMode) + { + case VK_PRESENT_MODE_IMMEDIATE_KHR: + break; + case VK_PRESENT_MODE_FIFO_KHR: + warning_message_present_mode_name = "FIFO"; + break; + case VK_PRESENT_MODE_MAILBOX_KHR: + warning_message_present_mode_name = "MAILBOX"; + break; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: + warning_message_present_mode_name = "FIFO_RELAXED"; + break; + case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: + case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: + case VK_PRESENT_MODE_RANGE_SIZE_KHR: + case VK_PRESENT_MODE_MAX_ENUM_KHR: + assert(!"bad present mode"); + break; + } + if(warning_message_present_mode_name) + std::cerr << warning_message_present_mode_name + << " present mode is not implemented; falling back to IMMEDIATE" + << std::endl; std::size_t unpadded_scanline_size = start_setup_results.image_pixel_size * start_setup_results.image_width; std::size_t padded_scanline_size = @@ -636,11 +676,16 @@ VkResult Xcb_wsi::get_surface_formats(VkIcdSurfaceBase *surface_, switch(start_setup_result.surface_format_group) { case Implementation::Surface_format_group::B8G8R8A8: - surface_formats = { + surface_formats = + { +#if 1 +#warning implement VK_FORMAT_B8G8R8A8_SRGB +#else { .format = VK_FORMAT_B8G8R8A8_SRGB, .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, }, +#endif { .format = VK_FORMAT_B8G8R8A8_UNORM, .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, -- 2.30.2