From 4b9c66a17e6074315da0ee82e5d3cf0a0d02e82f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 18 Sep 2017 23:12:26 -0700 Subject: [PATCH] SDL2's testvulkan works --- CMakeLists.txt | 3 + src/vulkan/CMakeLists.txt | 2 + src/vulkan/api_objects.cpp | 4 +- src/vulkan/api_objects.h | 182 +++++++++++++++++++++++++++------- src/vulkan_icd/vulkan_icd.cpp | 151 ++++++++++++++++++++++++++-- src/vulkan_icd/wsi.h | 1 + src/vulkan_icd/x11_wsi.cpp | 65 +++++++++++- 7 files changed, 359 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61d2bbc..072ddc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,9 @@ if(NOT LLVM_ENABLE_THREADS) endif() include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) +set(CMAKE_THREAD_PREFER_PTHREAD 1) +set(THREADS_PREFER_PTHREAD_FLAG 1) +find_package(Threads REQUIRED) if(WIN32) add_definitions(NOMINMAX) endif() diff --git a/src/vulkan/CMakeLists.txt b/src/vulkan/CMakeLists.txt index aba5ee0..0036e16 100644 --- a/src/vulkan/CMakeLists.txt +++ b/src/vulkan/CMakeLists.txt @@ -22,6 +22,8 @@ cmake_minimum_required(VERSION 3.3 FATAL_ERROR) set(sources vulkan.cpp api_objects.cpp) add_library(kazan_vulkan STATIC ${sources}) +target_link_libraries(kazan_vulkan + Threads::Threads) target_compile_definitions(kazan_vulkan PUBLIC VK_NO_PROTOTYPES) if(UNIX AND NOT CYGWIN AND NOT CMAKE_SYSTEM_NAME STREQUAL "Android") set(USE_X11 1) diff --git a/src/vulkan/api_objects.cpp b/src/vulkan/api_objects.cpp index 9549795..f0d4369 100644 --- a/src/vulkan/api_objects.cpp +++ b/src/vulkan/api_objects.cpp @@ -473,8 +473,8 @@ void Vulkan_image::clear(VkClearColorValue color) noexcept assert(descriptor.format == VK_FORMAT_B8G8R8A8_UNORM && "unimplemented image format"); assert(descriptor.mip_levels == 1 && "mipmapping is unimplemented"); assert(descriptor.array_layers == 1 && "array images are unimplemented"); - assert(descriptor.tiling == VK_IMAGE_TILING_LINEAR - && "non-linear image tiling is unimplemented"); +#warning implement non-linear image tiling + union { std::uint8_t bytes[4]; diff --git a/src/vulkan/api_objects.h b/src/vulkan/api_objects.h index 68b949f..61736aa 100644 --- a/src/vulkan/api_objects.h +++ b/src/vulkan/api_objects.h @@ -32,6 +32,7 @@ #include "util/system_memory_info.h" #include "util/constexpr_array.h" #include "util/optional.h" +#include "util/circular_queue.h" #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include namespace kazan { @@ -1531,48 +1533,16 @@ struct Vulkan_instance : public Vulkan_dispatchable_object -{ - struct Queue : public Vulkan_dispatchable_object - { - Vulkan_instance &instance; - Vulkan_physical_device &physical_device; - Vulkan_device &device; - explicit Queue(Vulkan_device &device) noexcept : instance(device.instance), - physical_device(device.physical_device), - device(device) - { - } - }; - Vulkan_instance &instance; - Vulkan_physical_device &physical_device; - VkPhysicalDeviceFeatures enabled_features; - static constexpr std::size_t queue_count = 1; - Queue queues[queue_count]; - Supported_extensions extensions; // includes both device and instance extensions - explicit Vulkan_device(Vulkan_physical_device &physical_device, - const VkPhysicalDeviceFeatures &enabled_features, - const Supported_extensions &extensions) noexcept - : instance(physical_device.instance), - physical_device(physical_device), - enabled_features(enabled_features), - queues{Queue(*this)}, - extensions(extensions) - { - } - void wait_idle() - { -#warning implement Vulkan_device::wait_idle - } - static util::variant, VkResult> create( - Vulkan_physical_device &physical_device, const VkDeviceCreateInfo &create_info); -}; +struct Vulkan_device; struct Vulkan_semaphore : public Vulkan_nondispatchable_object { void signal() // empty function for if semaphores are needed later { } + void wait() // empty function for if semaphores are needed later + { + } static std::unique_ptr create(Vulkan_device &device, const VkSemaphoreCreateInfo &create_info); }; @@ -1654,10 +1624,150 @@ public: const VkFence *fences, bool wait_for_all, std::uint64_t timeout); + VkResult wait(std::uint64_t timeout) + { + constexpr std::size_t fence_count = 1; + VkFence fences[fence_count] = { + to_handle(this), + }; + return wait_multiple(fence_count, fences, true, timeout); + } static std::unique_ptr create(Vulkan_device &device, const VkFenceCreateInfo &create_info); }; +struct Vulkan_device : public Vulkan_dispatchable_object +{ + struct Job + { + virtual ~Job() = default; + virtual void run() noexcept = 0; + }; + class Queue : public Vulkan_dispatchable_object + { + private: + std::mutex mutex; + std::condition_variable cond; + util::Static_circular_deque, 0x10> jobs; + std::thread executor_thread; + bool quit; + bool running_job; + + private: + void thread_fn() noexcept + { + std::unique_lock lock(mutex); + while(true) + { + if(jobs.empty()) + { + if(quit) + return; + cond.wait(lock); + continue; + } + auto job = std::move(jobs.front()); + bool was_full = jobs.full(); + jobs.pop_front(); + if(was_full) + cond.notify_all(); + running_job = true; + lock.unlock(); + job->run(); + lock.lock(); + running_job = false; + } + } + + public: + Queue() : mutex(), cond(), jobs(), executor_thread(), quit(false), running_job(false) + { + executor_thread = std::thread(&Queue::thread_fn, this); + } + ~Queue() + { + std::unique_lock lock(mutex); + quit = true; + cond.notify_all(); + lock.unlock(); + executor_thread.join(); + } + + private: + bool is_idle(std::unique_lock &lock) + { + if(!jobs.empty()) + return false; + if(running_job) + return false; + return true; + } + + public: + bool is_idle() + { + std::unique_lock lock(mutex); + return is_idle(lock); + } + void wait_idle() + { + std::unique_lock lock(mutex); + while(!is_idle(lock)) + cond.wait(lock); + } + void queue_job(std::unique_ptr job) + { + std::unique_lock lock(mutex); + while(jobs.full()) + cond.wait(lock); + bool was_idle = is_idle(lock); + jobs.push_back(std::move(job)); + if(was_idle) + cond.notify_all(); + } + void queue_fence_signal(Vulkan_fence &fence) + { + struct Signal_fence_job final : public Job + { + Vulkan_fence &fence; + explicit Signal_fence_job(Vulkan_fence &fence) noexcept : fence(fence) + { + } + virtual void run() noexcept override + { + fence.signal(); + } + }; + queue_job(std::make_unique(fence)); + } + }; + Vulkan_instance &instance; + Vulkan_physical_device &physical_device; + VkPhysicalDeviceFeatures enabled_features; + static constexpr std::size_t queue_count = 1; + std::unique_ptr queues[queue_count]; + Supported_extensions extensions; // includes both device and instance extensions + explicit Vulkan_device(Vulkan_physical_device &physical_device, + const VkPhysicalDeviceFeatures &enabled_features, + const Supported_extensions &extensions) noexcept + : instance(physical_device.instance), + physical_device(physical_device), + enabled_features(enabled_features), + queues{}, + extensions(extensions) + { + for(auto &queue : queues) + queue = std::make_unique(); + } + void wait_idle() + { + for(auto &queue : queues) + queue->wait_idle(); + } + static util::variant, VkResult> create( + Vulkan_physical_device &physical_device, const VkDeviceCreateInfo &create_info); +}; + struct Vulkan_image_descriptor { static constexpr VkImageCreateFlags supported_flags = diff --git a/src/vulkan_icd/vulkan_icd.cpp b/src/vulkan_icd/vulkan_icd.cpp index df682ce..1a88b65 100644 --- a/src/vulkan_icd/vulkan_icd.cpp +++ b/src/vulkan_icd/vulkan_icd.cpp @@ -245,26 +245,98 @@ extern "C" VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue(VkDevice device, assert(queue); auto *device_pointer = vulkan::Vulkan_device::from_handle(device); static_assert(vulkan::Vulkan_device::queue_count == 1, ""); - *queue = to_handle(&device_pointer->queues[0]); + *queue = to_handle(device_pointer->queues[0].get()); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(VkQueue queue, - uint32_t submitCount, - const VkSubmitInfo *pSubmits, + uint32_t submit_count, + const VkSubmitInfo *submits, VkFence fence) { -#warning finish implementing vkQueueSubmit - assert(!"vkQueueSubmit is not implemented"); + assert(queue); + assert(submit_count == 0 || submits); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + auto queue_pointer = vulkan::Vulkan_device::Queue::from_handle(queue); + for(std::size_t i = 0; i < submit_count; i++) + { + auto &submission = submits[i]; + assert(submission.sType == VK_STRUCTURE_TYPE_SUBMIT_INFO); + struct Run_submission_job final : public vulkan::Vulkan_device::Job + { + std::vector wait_semaphores; + std::vector command_buffers; + std::vector signal_semaphores; + Run_submission_job( + std::vector wait_semaphores, + std::vector command_buffers, + std::vector signal_semaphores) noexcept + : wait_semaphores(std::move(wait_semaphores)), + command_buffers(std::move(command_buffers)), + signal_semaphores(std::move(signal_semaphores)) + { + } + virtual void run() noexcept override + { + for(auto &i : wait_semaphores) + i->wait(); + for(auto &i : command_buffers) + i->run(); + for(auto &i : signal_semaphores) + i->signal(); + } + }; + std::vector wait_semaphores; + wait_semaphores.reserve(submission.waitSemaphoreCount); + for(std::uint32_t i = 0; i < submission.waitSemaphoreCount; i++) + { + assert(submission.pWaitSemaphores[i]); + wait_semaphores.push_back( + vulkan::Vulkan_semaphore::from_handle(submission.pWaitSemaphores[i])); + } + std::vector command_buffers; + command_buffers.reserve(submission.commandBufferCount); + for(std::uint32_t i = 0; i < submission.commandBufferCount; i++) + { + assert(submission.pCommandBuffers[i]); + command_buffers.push_back( + vulkan::Vulkan_command_buffer::from_handle(submission.pCommandBuffers[i])); + } + std::vector signal_semaphores; + signal_semaphores.reserve(submission.signalSemaphoreCount); + for(std::uint32_t i = 0; i < submission.signalSemaphoreCount; i++) + { + assert(submission.pSignalSemaphores[i]); + signal_semaphores.push_back( + vulkan::Vulkan_semaphore::from_handle(submission.pSignalSemaphores[i])); + } + queue_pointer->queue_job( + std::make_unique(std::move(wait_semaphores), + std::move(command_buffers), + std::move(signal_semaphores))); + } + if(fence) + queue_pointer->queue_fence_signal(*vulkan::Vulkan_fence::from_handle(fence)); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle(VkQueue queue) { -#warning finish implementing vkQueueWaitIdle - assert(!"vkQueueWaitIdle is not implemented"); + assert(queue); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + auto queue_pointer = vulkan::Vulkan_device::Queue::from_handle(queue); + queue_pointer->wait_idle(); + return VK_SUCCESS; + }); } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle(VkDevice device) { + assert(device); return vulkan_icd::catch_exceptions_and_return_result( [&]() { @@ -1770,10 +1842,69 @@ extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR(VkDevice device, } extern "C" VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR(VkQueue queue, - const VkPresentInfoKHR *pPresentInfo) + const VkPresentInfoKHR *present_info) { -#warning finish implementing vkQueuePresentKHR - assert(!"vkQueuePresentKHR is not implemented"); + assert(queue); + assert(present_info); + assert(present_info->sType == VK_STRUCTURE_TYPE_PRESENT_INFO_KHR); + assert(present_info->waitSemaphoreCount == 0 || present_info->pWaitSemaphores); + assert(present_info->swapchainCount > 0); + assert(present_info->pSwapchains); + assert(present_info->pImageIndices); + return vulkan_icd::catch_exceptions_and_return_result( + [&]() + { + auto *queue_pointer = vulkan::Vulkan_device::Queue::from_handle(queue); + if(present_info->waitSemaphoreCount > 0) + { + std::vector semaphores; + semaphores.reserve(present_info->waitSemaphoreCount); + for(std::uint32_t i = 0; i < present_info->waitSemaphoreCount; i++) + { + assert(present_info->pWaitSemaphores[i]); + semaphores.push_back( + vulkan::Vulkan_semaphore::from_handle(present_info->pWaitSemaphores[i])); + } + struct Wait_for_semaphores_job final : public vulkan::Vulkan_device::Job + { + std::vector semaphores; + explicit Wait_for_semaphores_job( + std::vector semaphores) noexcept + : semaphores(std::move(semaphores)) + { + } + virtual void run() noexcept override + { + for(auto &i : semaphores) + i->wait(); + } + }; + queue_pointer->queue_job( + std::make_unique(std::move(semaphores))); + } + VkResult retval = VK_SUCCESS; + for(std::uint32_t i = 0; i < present_info->swapchainCount; i++) + { + auto *swapchain = + vulkan_icd::Vulkan_swapchain::from_handle(present_info->pSwapchains[i]); + assert(swapchain); + VkResult present_result = + swapchain->queue_present(present_info->pImageIndices[i], *queue_pointer); + if(present_result == VK_ERROR_DEVICE_LOST || retval == VK_ERROR_DEVICE_LOST) + retval = VK_ERROR_DEVICE_LOST; + else if(present_result == VK_ERROR_SURFACE_LOST_KHR + || retval == VK_ERROR_SURFACE_LOST_KHR) + retval = VK_ERROR_SURFACE_LOST_KHR; + else if(present_result == VK_ERROR_OUT_OF_DATE_KHR + || retval == VK_ERROR_OUT_OF_DATE_KHR) + retval = VK_ERROR_OUT_OF_DATE_KHR; + else if(present_result == VK_SUBOPTIMAL_KHR || retval == VK_SUBOPTIMAL_KHR) + retval = VK_SUBOPTIMAL_KHR; + if(present_info->pResults) + present_info->pResults[i] = present_result; + } + return retval; + }); } namespace kazan diff --git a/src/vulkan_icd/wsi.h b/src/vulkan_icd/wsi.h index ad69747..1fe2f1b 100644 --- a/src/vulkan_icd/wsi.h +++ b/src/vulkan_icd/wsi.h @@ -54,6 +54,7 @@ public: vulkan::Vulkan_semaphore *semaphore, vulkan::Vulkan_fence *fence, std::uint32_t &returned_image_index) = 0; + virtual VkResult queue_present(std::uint32_t image_index, vulkan::Vulkan_device::Queue &queue) = 0; }; struct Wsi diff --git a/src/vulkan_icd/x11_wsi.cpp b/src/vulkan_icd/x11_wsi.cpp index 9656ca4..08c7e6b 100644 --- a/src/vulkan_icd/x11_wsi.cpp +++ b/src/vulkan_icd/x11_wsi.cpp @@ -501,6 +501,8 @@ struct Xcb_wsi::Implementation util::Static_circular_deque presenting_image_queue; std::uint32_t swapchain_width; std::uint32_t swapchain_height; + Gc gc; + unsigned window_depth; explicit Swapchain(Start_setup_results start_setup_results, xcb_connection_t *connection, xcb_window_t window, @@ -510,7 +512,9 @@ struct Xcb_wsi::Implementation window(window), shm_is_supported(start_setup_results.shm_is_supported), status(Status::Good), - presenting_image_queue() + presenting_image_queue(), + gc(std::move(start_setup_results.gc)), + window_depth(start_setup_results.window_depth) { assert(create_info.sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); #warning formats other than VK_FORMAT_B8G8R8A8_UNORM are unimplemented @@ -722,6 +726,65 @@ struct Xcb_wsi::Implementation return VK_SUCCESS; } } + virtual VkResult queue_present(std::uint32_t image_index, + vulkan::Vulkan_device::Queue &queue) override + { + assert(image_index < images.size()); + switch(status) + { + case Status::No_surface: + case Status::Setup_failed: + return VK_ERROR_SURFACE_LOST_KHR; + case Status::Out_of_date: + return VK_ERROR_OUT_OF_DATE_KHR; + case Status::Good: + break; + } + auto &image = get_image(image_index); + assert(image.owner == Image_owner::Application); + // wait for rendering to catch up + { + vulkan::Vulkan_fence fence(0); + queue.queue_fence_signal(fence); + fence.wait(-1); + } + + if(shm_is_supported) + { + xcb_copy_area(connection, + image.pixmap.get(), + window, + gc.get(), + 0, + 0, + 0, + 0, + swapchain_width, + swapchain_height); + } + else + { + std::size_t image_size = image.descriptor.get_memory_size(); + assert(static_cast(image_size) == image_size); + xcb_put_image(connection, + XCB_IMAGE_FORMAT_Z_PIXMAP, + window, + gc.get(), + swapchain_width, + swapchain_height, + 0, + 0, + 0, + window_depth, + image_size, + static_cast(image.memory.get())); + } + image.get_geometry_cookie = xcb_get_geometry(connection, window); + image.owner = Image_owner::Presentation_engine; + presenting_image_queue.push_back(image_index); + xcb_flush(connection); + return VK_SUCCESS; + } }; }; -- 2.30.2