diff --git a/curve.cc b/curve.cc index 55d2c14..baddc5f 100644 --- a/curve.cc +++ b/curve.cc @@ -146,6 +146,8 @@ void Curve::Register( vk::UniqueRenderPass *render_pass, vk::UniquePipelineCache *pipeline_cache) { + points_.clear(); + // Instantiate the shaders vk::UniqueShaderModule vertex = context->device->createShaderModuleUnique( diff --git a/scene.cc b/scene.cc index c3de31e..8fb990a 100644 --- a/scene.cc +++ b/scene.cc @@ -16,7 +16,6 @@ // // This file contains the basic ingredients to render a basic wireframed scene. // This simple scene allows you to add meshes and a freely "movable" camera. - #include #include #include @@ -36,112 +35,123 @@ #define FENCE_TIMEOUT 100000000 -Scene::Scene(space::core::VkAppContext *vk_ctx) - : vk_ctx_(vk_ctx), r_ctx_(InitRenderingContext()), - current_buffer_(0), +Scene::Scene(space::core::VkAppContext *vk_ctx, const QueryExtentCallback &fn) + : vk_ctx_(vk_ctx), QueryExtent(fn), current_buffer_(0), draw_fence_(vk_ctx->device->createFenceUnique(vk::FenceCreateInfo())), camera_{glm::vec3(0.0f, 0.0f, -15.0f), glm::vec3(0.0f, 2.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)}, - projection_matrices_(UpdateProjectionMatrices()) {} + projection_matrices_({0}) {} -Scene::RenderingContext Scene::InitRenderingContext() { - vk::PhysicalDevice &physical_device = vk_ctx_->physical_device; - space::core::SurfaceData &surface_data = vk_ctx_->surface_data; +void Scene::Init() { vk::UniqueDevice &device = vk_ctx_->device; + const uint32_t graphics_queue_family_index = vk_ctx_->graphics_queue_family_index; + const uint32_t present_queue_family_index = vk_ctx_->present_queue_family_index; + // For multi threaded applications, we should create a command pool // for each thread. For this example, we just need one as we go // with async single core app. + command_pool_ = + space::core::CreateCommandPool(vk_ctx_->device, graphics_queue_family_index); + graphics_queue_ = device->getQueue(graphics_queue_family_index, 0); + present_queue_ = device->getQueue(present_queue_family_index, 0); + + descriptor_set_layout_ = + space::core::CreateDescriptorSetLayout( + device, { {vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex} }); + pipeline_layout_ = + device->createPipelineLayoutUnique( + vk::PipelineLayoutCreateInfo( + vk::PipelineLayoutCreateFlags(), 1, &descriptor_set_layout_.get())); + + pipeline_cache_ = + device->createPipelineCacheUnique(vk::PipelineCacheCreateInfo()); + + CreateSwapChainContext(); +} + +void Scene::CreateSwapChainContext() { + vk::PhysicalDevice &physical_device = vk_ctx_->physical_device; + vk::UniqueSurfaceKHR &surface = vk_ctx_->surface; + vk::UniqueDevice &device = vk_ctx_->device; const uint32_t graphics_queue_family_index = vk_ctx_->graphics_queue_family_index; const uint32_t present_queue_family_index = vk_ctx_->present_queue_family_index; - vk::UniqueCommandPool command_pool = - space::core::CreateCommandPool(vk_ctx_->device, graphics_queue_family_index); + const vk::Extent2D extent = QueryExtent(); vk::UniqueCommandBuffer command_buffer = std::move( device->allocateCommandBuffersUnique( vk::CommandBufferAllocateInfo( - command_pool.get(), - vk::CommandBufferLevel::ePrimary, 1)).front()); + *command_pool_, vk::CommandBufferLevel::ePrimary, 1)).front()); - vk::Queue graphics_queue = - device->getQueue(graphics_queue_family_index, 0); - vk::Queue present_queue = - device->getQueue(present_queue_family_index, 0); + // Wait device to be idle before destroying everything + if (swap_chain_context_) + device->waitIdle(); space::core::SwapChainData swap_chain_data( - physical_device, device, *surface_data.surface, surface_data.extent, + physical_device, device, *surface, extent, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc, - vk::UniqueSwapchainKHR(), graphics_queue_family_index, - present_queue_family_index); + swap_chain_context_ ? std::move(swap_chain_context_->swap_chain_data.swap_chain) : vk::UniqueSwapchainKHR(), + graphics_queue_family_index, present_queue_family_index); space::core::DepthBufferData depth_buffer_data( - physical_device, device, vk::Format::eD16Unorm, surface_data.extent); + physical_device, device, vk::Format::eD16Unorm, swap_chain_data.extent); space::core::BufferData uniform_buffer_data( physical_device, device, sizeof(glm::mat4x4), vk::BufferUsageFlagBits::eUniformBuffer); - vk::UniqueDescriptorSetLayout descriptor_set_layout = - space::core::CreateDescriptorSetLayout( - device, { {vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex} }); - vk::UniquePipelineLayout pipeline_layout = - device->createPipelineLayoutUnique( - vk::PipelineLayoutCreateInfo( - vk::PipelineLayoutCreateFlags(), 1, &descriptor_set_layout.get())); - vk::UniqueRenderPass render_pass = space::core::CreateRenderPass( device, space::core::PickSurfaceFormat( physical_device.getSurfaceFormatsKHR( - surface_data.surface.get()))->format, depth_buffer_data.format); + *surface))->format, depth_buffer_data.format); std::vector framebuffers = space::core::CreateFramebuffers( device, render_pass, swap_chain_data.image_views, - depth_buffer_data.image_view, surface_data.extent); + depth_buffer_data.image_view, swap_chain_data.extent); vk::UniqueDescriptorPool descriptor_pool = space::core::CreateDescriptorPool(device, { {vk::DescriptorType::eUniformBuffer, 1} }); vk::UniqueDescriptorSet descriptor_set = std::move( device->allocateDescriptorSetsUnique( - vk::DescriptorSetAllocateInfo(*descriptor_pool, 1, &*descriptor_set_layout)).front()); + vk::DescriptorSetAllocateInfo(*descriptor_pool, 1, &*descriptor_set_layout_)).front()); space::core::UpdateDescriptorSets( device, descriptor_set, {{vk::DescriptorType::eUniformBuffer, uniform_buffer_data.buffer, vk::UniqueBufferView()}}); - vk::UniquePipelineCache pipeline_cache = - device->createPipelineCacheUnique(vk::PipelineCacheCreateInfo()); + struct SwapChainContext *swap_chain_context = new SwapChainContext{ + std::move(command_buffer), std::move(swap_chain_data), std::move(depth_buffer_data), + std::move(uniform_buffer_data), std::move(render_pass), std::move(framebuffers), + std::move(descriptor_pool), std::move(descriptor_set)}; - Scene::RenderingContext r_ctx{ - std::move(command_pool), std::move(command_buffer), graphics_queue, present_queue, - std::move(swap_chain_data), std::move(depth_buffer_data), std::move(uniform_buffer_data), - std::move(descriptor_set_layout), std::move(pipeline_layout), - std::move(render_pass), std::move(framebuffers), std::move(descriptor_pool), - std::move(descriptor_set), std::move(pipeline_cache)}; + swap_chain_context_.reset(swap_chain_context); - return r_ctx; + for (const auto entity : entities_) { + entity->Register(vk_ctx_, &pipeline_layout_, &swap_chain_context_->render_pass, + &pipeline_cache_); + } } void Scene::AddEntity(space::Entity *entity) { // Initialize entity - entity->Register(vk_ctx_, &r_ctx_.pipeline_layout, &r_ctx_.render_pass, - &r_ctx_.pipeline_cache); + entity->Register(vk_ctx_, &pipeline_layout_, &swap_chain_context_->render_pass, + &pipeline_cache_); entities_.push_back(entity); } void Scene::SubmitRendering() { const vk::UniqueDevice &device = vk_ctx_->device; - const space::core::SwapChainData &swap_chain_data = r_ctx_.swap_chain_data; - const vk::Queue &graphics_queue = r_ctx_.graphics_queue; - const vk::UniqueCommandBuffer &command_buffer = r_ctx_.command_buffer; - const vk::UniqueRenderPass &render_pass = r_ctx_.render_pass; - const std::vector &framebuffers = r_ctx_.framebuffers; - const vk::UniquePipelineLayout &pipeline_layout = r_ctx_.pipeline_layout; - const vk::UniqueDescriptorSet &descriptor_set = r_ctx_.descriptor_set; - const space::core::SurfaceData &surface_data = vk_ctx_->surface_data; - const space::core::BufferData &uniform_buffer_data = r_ctx_.uniform_buffer_data; + const space::core::SwapChainData &swap_chain_data = swap_chain_context_->swap_chain_data; + const vk::Queue &graphics_queue = graphics_queue_; + const vk::UniqueCommandBuffer &command_buffer = swap_chain_context_->command_buffer; + const vk::UniqueRenderPass &render_pass = swap_chain_context_->render_pass; + const std::vector &framebuffers = swap_chain_context_->framebuffers; + const vk::UniquePipelineLayout &pipeline_layout = pipeline_layout_; + const vk::UniqueDescriptorSet &descriptor_set = swap_chain_context_->descriptor_set; + const space::core::BufferData &uniform_buffer_data = swap_chain_context_->uniform_buffer_data; // Update the projection matrices with the current values of camera, model, fov, etc.. projection_matrices_ = UpdateProjectionMatrices(); @@ -153,13 +163,27 @@ void Scene::SubmitRendering() { // Get the index of the next available swapchain image: vk::UniqueSemaphore imageAcquiredSemaphore = device->createSemaphoreUnique(vk::SemaphoreCreateInfo()); - vk::ResultValue res = - device->acquireNextImageKHR( - swap_chain_data.swap_chain.get(), FENCE_TIMEOUT, - imageAcquiredSemaphore.get(), nullptr); - assert(res.result == vk::Result::eSuccess); - assert(res.value < r_ctx_.framebuffers.size()); - current_buffer_ = res.value; + + bool out_of_date = false; + try { + vk::ResultValue res = + device->acquireNextImageKHR( + swap_chain_data.swap_chain.get(), FENCE_TIMEOUT, + imageAcquiredSemaphore.get(), nullptr); + if (res.result == vk::Result::eSuboptimalKHR) + out_of_date = true; + assert(res.value < swap_chain_context_->framebuffers.size()); + current_buffer_ = res.value; + } catch (vk::OutOfDateKHRError &) { + out_of_date = true; + } + if (out_of_date) { + // Re-create the swapchain context. + CreateSwapChainContext(); + // Re-submit rendering + return SubmitRendering(); + } + command_buffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlags())); vk::ClearValue clear_values[2]; @@ -169,7 +193,7 @@ void Scene::SubmitRendering() { vk::ClearDepthStencilValue(1.0f, 0); vk::RenderPassBeginInfo renderPassBeginInfo( render_pass.get(), framebuffers[current_buffer_].get(), - vk::Rect2D(vk::Offset2D(0, 0), surface_data.extent), 2, clear_values); + vk::Rect2D(vk::Offset2D(0, 0), swap_chain_data.extent), 2, clear_values); command_buffer->beginRenderPass( renderPassBeginInfo, vk::SubpassContents::eInline); @@ -180,10 +204,10 @@ void Scene::SubmitRendering() { command_buffer->setViewport( 0, vk::Viewport( 0.0f, 0.0f, - static_cast(surface_data.extent.width), - static_cast(surface_data.extent.height), 0.0f, 1.0f)); + static_cast(swap_chain_data.extent.width), + static_cast(swap_chain_data.extent.height), 0.0f, 1.0f)); command_buffer->setScissor( - 0, vk::Rect2D(vk::Offset2D(0, 0), surface_data.extent)); + 0, vk::Rect2D(vk::Offset2D(0, 0), swap_chain_data.extent)); for (const auto entity : entities_) { entity->Draw(&command_buffer); @@ -200,19 +224,23 @@ void Scene::SubmitRendering() { void Scene::Present() { vk::UniqueDevice &device = vk_ctx_->device; - space::core::SwapChainData &swap_chain_data = r_ctx_.swap_chain_data; - vk::Queue &present_queue = r_ctx_.present_queue; + space::core::SwapChainData &swap_chain_data = swap_chain_context_->swap_chain_data; + vk::Queue &present_queue = present_queue_; while (vk::Result::eTimeout == device->waitForFences(draw_fence_.get(), VK_TRUE, FENCE_TIMEOUT)) { usleep(1000); } - present_queue.presentKHR( - vk::PresentInfoKHR(0, nullptr, 1, &swap_chain_data.swap_chain.get(), ¤t_buffer_)); -} + try { + present_queue.presentKHR( + vk::PresentInfoKHR(0, nullptr, 1, &swap_chain_data.swap_chain.get(), ¤t_buffer_)); + } catch (vk::OutOfDateKHRError &) { + // Re-create the swapchain context. + CreateSwapChainContext(); + } +} struct Scene::Projection Scene::UpdateProjectionMatrices() { - - vk::Extent2D &extent = vk_ctx_->surface_data.extent; + vk::Extent2D extent = swap_chain_context_->swap_chain_data.extent; float fov = glm::radians(60.0f); const auto aspect_ratio = diff --git a/scene.h b/scene.h index 853fd8b..52806d2 100644 --- a/scene.h +++ b/scene.h @@ -42,8 +42,12 @@ struct CameraControls { // perform rendering of the entities. class Scene { public: - Scene(space::core::VkAppContext *context); + typedef std::function QueryExtentCallback; + + Scene(space::core::VkAppContext *context, const QueryExtentCallback &fn); + + void Init(); void AddEntity(space::Entity *entity); void Input(CameraControls &input); void SubmitRendering(); @@ -51,13 +55,23 @@ class Scene { private: space::core::VkAppContext *const vk_ctx_; + const QueryExtentCallback QueryExtent; - struct RenderingContext { - vk::UniqueCommandPool command_pool; - vk::UniqueCommandBuffer command_buffer; - vk::Queue graphics_queue; - vk::Queue present_queue; + vk::UniqueCommandPool command_pool_; + vk::Queue graphics_queue_; + vk::Queue present_queue_; + + vk::UniqueDescriptorSetLayout descriptor_set_layout_; + + // The pipeline layout used to describe + // how descriptors should be used. + vk::UniquePipelineLayout pipeline_layout_; + vk::UniquePipelineCache pipeline_cache_; + // Define the set of objects to be recreated + // in case of an out-of-date swapchain. + struct SwapChainContext { + vk::UniqueCommandBuffer command_buffer; space::core::SwapChainData swap_chain_data; // Depth buffer data. Contains the resulting @@ -68,23 +82,17 @@ class Scene { // and other shared buffers. space::core::BufferData uniform_buffer_data; - vk::UniqueDescriptorSetLayout descriptor_set_layout; - - // The pipeline layout used to describe - // how descriptors should be used. - vk::UniquePipelineLayout pipeline_layout; - // We are using a single render pass vk::UniqueRenderPass render_pass; std::vector framebuffers; vk::UniqueDescriptorPool descriptor_pool; vk::UniqueDescriptorSet descriptor_set; - vk::UniquePipelineCache pipeline_cache; }; - // Initialize the rendering context - struct RenderingContext InitRenderingContext(); - RenderingContext r_ctx_; + std::unique_ptr swap_chain_context_; + + // Creates a new swapchain and returns the old one. + void CreateSwapChainContext(); uint32_t current_buffer_; diff --git a/space.cc b/space.cc index 84545e2..5a046d8 100644 --- a/space.cc +++ b/space.cc @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see +#include #include #include #include @@ -28,6 +29,13 @@ #include "gamepad.h" #include "curve.h" +std::optional get_xlib_window_extent(Display *display, Window window) { + XWindowAttributes attrs; + if (XGetWindowAttributes(display, window, &attrs)) { + return vk::Extent2D(attrs.width, attrs.height); + } + return {}; +} static void gamepad2camera( CameraControls *camera_controls, const struct EventData &data) { @@ -138,7 +146,13 @@ int main(int argc, char *argv[]) { // as we want to avoid its destruction after // the display is closed with XCloseDisplay(). { - Scene scene(&vk_ctx); + Scene scene(&vk_ctx, [display, window] () { + XWindowAttributes attrs; + XGetWindowAttributes(display, window, &attrs); + return vk::Extent2D(attrs.width, attrs.height); + }); + + scene.Init(); ReferenceGrid reference_grid; Curve curve; @@ -147,6 +161,7 @@ int main(int argc, char *argv[]) { XSelectInput(display, window, ExposureMask | KeyPressMask + | StructureNotifyMask | PointerMotionMask); XMapWindow(display, window); XFlush(display); @@ -188,8 +203,11 @@ int main(int argc, char *argv[]) { if (FD_ISSET(x11_fd, &read_fds)) { while(XPending(display)) { XNextEvent(display, &e); - if (e.type == KeyPress) { + + switch (e.type) { + case KeyPress: exit = true; + break; } } } diff --git a/vulkan-core.cc b/vulkan-core.cc index 631ecb1..9fc248e 100644 --- a/vulkan-core.cc +++ b/vulkan-core.cc @@ -74,14 +74,6 @@ std::optional> FindGraphicsAndPresentQueueFamilyIn return {}; } -std::optional get_xlib_window_extent(Display *display, Window window) { - XWindowAttributes attrs; - if (XGetWindowAttributes(display, window, &attrs)) { - return vk::Extent2D(attrs.width, attrs.height); - } - return {}; -} - vk::UniqueDevice CreateDevice( vk::PhysicalDevice physical_device, uint32_t queue_family_index, std::vector const& extensions = {}, @@ -321,23 +313,15 @@ namespace space { // possibly configurable policy. vk::PhysicalDevice physical_device = instance->enumeratePhysicalDevices().front(); - vk::Extent2D extent; - if (auto res = get_xlib_window_extent(display, window)) { - extent = res.value(); - } else { - fprintf(stderr, "Coudldn't guess the xlib window extent."); - return {}; - } // Init the surface - struct SurfaceData surface_data = { + vk::UniqueSurfaceKHR surface = instance->createXlibSurfaceKHRUnique( - vk::XlibSurfaceCreateInfoKHR(vk::XlibSurfaceCreateFlagsKHR(), display, window)), - extent}; + vk::XlibSurfaceCreateInfoKHR(vk::XlibSurfaceCreateFlagsKHR(), display, window)); // Find devices for present and graphics. std::pair graphics_and_present_queue_family_index; if (const auto o = FindGraphicsAndPresentQueueFamilyIndex( - physical_device, *surface_data.surface)) { + physical_device, *surface)) { graphics_and_present_queue_family_index = o.value(); } else { fprintf(stderr, "Couldn't find suitable Present or Graphics queues."); @@ -356,9 +340,12 @@ namespace space { VULKAN_HPP_DEFAULT_DISPATCHER.init(*device); return VkAppContext{ - std::move(dl), std::move(instance), std::move(device), + std::move(dl), + std::move(instance), + std::move(surface), + std::move(device), std::move(debug_utils_messenger), - physical_device, std::move(surface_data), + physical_device, graphics_and_present_queue_family_index.first, graphics_and_present_queue_family_index.second}; } diff --git a/vulkan-core.h b/vulkan-core.h index b1ab915..273eb5c 100644 --- a/vulkan-core.h +++ b/vulkan-core.h @@ -14,7 +14,7 @@ // limitations under the License. // Modifications copyright (C) 2020 Leonardo Romor // -// This file contains the represents an intermediate interface simplify vulkan. +// This file contains the represents an intermediate interface to simplify vulkan. #ifndef __SPACE_CORE_H_ #define __SPACE_CORE_H_ @@ -47,12 +47,9 @@ VULKAN_HPP_INLINE TargetType checked_cast(SourceType value) { namespace space { namespace core { - // Vulkan initialization routines - struct SurfaceData { - vk::UniqueSurfaceKHR surface; - vk::Extent2D extent; - }; - + // Hold the Vulkan configuration data + // such as application name, engine, + // and requested instance layers and extensions. struct VkAppConfig { const char *app_name; const char *engine_name; @@ -60,13 +57,18 @@ namespace space { std::vector instance_extensions; }; + // Holds the vulkan datacstructures + // used to represent the vulkan implementation, + // instantiation and configuration. It does not + // include any rendering related vullkan calls or + // data structures. struct VkAppContext { vk::DynamicLoader dynamic_loader; vk::UniqueInstance instance; + vk::UniqueSurfaceKHR surface; vk::UniqueDevice device; vk::UniqueDebugUtilsMessengerEXT debug_utils_messenger; vk::PhysicalDevice physical_device; - SurfaceData surface_data; uint32_t graphics_queue_family_index; uint32_t present_queue_family_index; }; @@ -86,6 +88,7 @@ namespace space { uint32_t graphics_family_index, uint32_t present_family_index); vk::Format color_format; + vk::Extent2D extent; vk::UniqueSwapchainKHR swap_chain; std::vector images; std::vector image_views; diff --git a/vulkan-rendering.cc b/vulkan-rendering.cc index 015bc1f..3c5e745 100644 --- a/vulkan-rendering.cc +++ b/vulkan-rendering.cc @@ -19,6 +19,7 @@ // found here. #include +#include #include "vulkan-core.h" @@ -82,8 +83,7 @@ namespace space { vk::SurfaceKHR const& surface, vk::Extent2D const& extent, vk::ImageUsageFlags usage, vk::UniqueSwapchainKHR const& old_swap_chain, uint32_t graphics_queue_family_index, uint32_t present_queue_family_index) { - - vk::SurfaceFormatKHR surface_format =PickSurfaceFormat( + vk::SurfaceFormatKHR surface_format = ::PickSurfaceFormat( physical_device.getSurfaceFormatsKHR(surface)).value(); color_format = surface_format.format; @@ -137,6 +137,7 @@ namespace space { swapChainCreateInfo.pQueueFamilyIndices = queueFamilyIndices; } swap_chain = device->createSwapchainKHRUnique(swapChainCreateInfo); + this->extent = swap_chain_extent; images = device->getSwapchainImagesKHR(swap_chain.get());