implement vulkan fences and command pools; add more validation to swapchain creation
authorJacob Lifshay <programmerjake@gmail.com>
Sat, 16 Sep 2017 01:43:32 +0000 (18:43 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Sat, 16 Sep 2017 01:43:32 +0000 (18:43 -0700)
src/vulkan/api_objects.cpp
src/vulkan/api_objects.h
src/vulkan_icd/vulkan_icd.cpp
src/vulkan_icd/x11_wsi.cpp

index 199f7daffee19d6a4dd2ee505b8f3cd0d039e93c..6591b3dd22df9ae85c3968ea7a6872e4c6910581 100644 (file)
@@ -23,6 +23,9 @@
 #include "api_objects.h"
 #include "util/optional.h"
 #include <iostream>
+#include <type_traits>
+#include <vector>
+#include <algorithm>
 
 namespace kazan
 {
@@ -366,10 +369,152 @@ std::unique_ptr<Vulkan_semaphore> Vulkan_semaphore::create(Vulkan_device &device
     return std::make_unique<Vulkan_semaphore>();
 }
 
-std::unique_ptr<Vulkan_image> 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<Duration> wait_duration; // nullopt means infinite timeout
+    if(timeout <= static_cast<std::uint64_t>(
+                      std::chrono::duration_cast<std::chrono::nanoseconds>(max_wait_time).count()))
+    {
+        wait_duration = std::chrono::duration_cast<Duration>(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<Time_point> 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<std::list<Waiter *>::iterator> iters;
+    iters.reserve(fence_count);
+    struct Fence_cleanup
+    {
+        std::vector<std::list<Waiter *>::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<std::mutex> 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<std::mutex> 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> 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<Vulkan_fence>(create_info.flags);
+}
+
+std::unique_ptr<Vulkan_image> Vulkan_image::create(Vulkan_device &device,
+                                                   const VkImageCreateInfo &create_info)
 {
 #warning finish implementing Vulkan_image::create
     return std::make_unique<Vulkan_image>();
 }
+
+Vulkan_command_buffer::Vulkan_command_buffer(
+    std::list<std::unique_ptr<Vulkan_command_buffer>>::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<std::unique_ptr<Vulkan_command_buffer>> 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<Vulkan_command_buffer>(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> 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<Vulkan_command_pool>();
+}
 }
 }
index 2e8adb47af03c26bb75ab2cef466a51373e3f4c9..cf44431d314e65ce11966f7dc7fd0ca07863df19 100644 (file)
 #include "util/variant.h"
 #include "util/system_memory_info.h"
 #include "util/constexpr_array.h"
+#include "util/optional.h"
 #include <memory>
 #include <cassert>
 #include <chrono>
 #include <limits>
+#include <vector>
+#include <list>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
 
 namespace kazan
 {
@@ -1568,6 +1574,84 @@ struct Vulkan_semaphore : public Vulkan_nondispatchable_object<Vulkan_semaphore,
                                                     const VkSemaphoreCreateInfo &create_info);
 };
 
+class Vulkan_fence : public Vulkan_nondispatchable_object<Vulkan_fence, VkFence>
+{
+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<std::mutex> lock_it(lock);
+            if(wait_count != 0)
+            {
+                wait_count--;
+                if(notify_condition_variable && wait_count == 0)
+                    cond.notify_all();
+            }
+        }
+        bool wait(util::optional<std::chrono::steady_clock::time_point> end_time)
+        {
+            std::unique_lock<std::mutex> 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<Waiter *> waiters;
+
+public:
+    explicit Vulkan_fence(VkFenceCreateFlags flags)
+        : lock(), signaled(flags & VK_FENCE_CREATE_SIGNALED_BIT), waiters()
+    {
+    }
+    bool is_signaled()
+    {
+        std::unique_lock<std::mutex> lock_it(lock);
+        return signaled;
+    }
+    void set_signaled(bool new_signaled)
+    {
+        std::unique_lock<std::mutex> 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<Vulkan_fence> create(Vulkan_device &device,
+                                                const VkFenceCreateInfo &create_info);
+};
+
 struct Vulkan_image : public Vulkan_nondispatchable_object<Vulkan_image, VkImage>
 {
     virtual ~Vulkan_image() = default;
@@ -1575,6 +1659,51 @@ struct Vulkan_image : public Vulkan_nondispatchable_object<Vulkan_image, VkImage
     static std::unique_ptr<Vulkan_image> create(Vulkan_device &device,
                                                 const VkImageCreateInfo &create_info);
 };
+
+struct Vulkan_command_pool;
+
+struct Vulkan_command_buffer
+    : public Vulkan_dispatchable_object<Vulkan_command_buffer, VkCommandBuffer>
+{
+    std::list<std::unique_ptr<Vulkan_command_buffer>>::iterator iter;
+    Vulkan_command_pool &command_pool;
+    Vulkan_device &device;
+    Vulkan_command_buffer(std::list<std::unique_ptr<Vulkan_command_buffer>>::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<Vulkan_command_pool, VkCommandPool>
+{
+    std::list<std::unique_ptr<Vulkan_command_buffer>> 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<Vulkan_command_pool> create(Vulkan_device &device,
+                                                       const VkCommandPoolCreateInfo &create_info);
+};
 }
 }
 
index 66ccca75debbaa9dab2f8b06bbf99242d7ce96a0..aa6363b0b18e88f70a588743f03ffb76962948af 100644 (file)
@@ -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
index 963a7d15a363c7b3e3ac649b63961fa6c4d9e669..c31f19a31ccf27adfe2a604c17ee82787410776f 100644 (file)
@@ -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,