diff --git a/scene.cc b/scene.cc index 778c29c..779a90a 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,11 +35,11 @@ #define FENCE_TIMEOUT 100000000 -Scene::Scene(space::core::VkAppContext *vk_ctx) - : vk_ctx_(vk_ctx), 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}) {} void Scene::Init() { vk::UniqueDevice &device = vk_ctx_->device; @@ -67,30 +66,35 @@ void Scene::Init() { pipeline_cache_ = device->createPipelineCacheUnique(vk::PipelineCacheCreateInfo()); - CreateSwapChain(); + CreateSwapChainContext(); } -void Scene::CreateSwapChain() { +void Scene::CreateSwapChainContext() { vk::PhysicalDevice &physical_device = vk_ctx_->physical_device; - space::core::SurfaceData &surface_data = vk_ctx_->surface_data; + 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; + const vk::Extent2D extent = QueryExtent(); vk::UniqueCommandBuffer command_buffer = std::move( device->allocateCommandBuffersUnique( vk::CommandBufferAllocateInfo( *command_pool_, vk::CommandBufferLevel::ePrimary, 1)).front()); + // 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), @@ -100,12 +104,12 @@ void Scene::CreateSwapChain() { 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} }); @@ -127,6 +131,11 @@ void Scene::CreateSwapChain() { std::move(descriptor_pool), std::move(descriptor_set)}; swap_chain_context_.reset(swap_chain_context); + + for (const auto entity : entities_) { + entity->Register(vk_ctx_, &pipeline_layout_, &swap_chain_context_->render_pass, + &pipeline_cache_); + } } void Scene::AddEntity(space::Entity *entity) { @@ -145,7 +154,6 @@ void Scene::SubmitRendering() { 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::SurfaceData &surface_data = vk_ctx_->surface_data; 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.. @@ -158,13 +166,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 < swap_chain_context_->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]; @@ -174,7 +196,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); @@ -185,10 +207,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); @@ -210,14 +232,18 @@ void Scene::Present() { 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 484d3e3..52806d2 100644 --- a/scene.h +++ b/scene.h @@ -42,7 +42,10 @@ 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); @@ -52,6 +55,8 @@ class Scene { private: space::core::VkAppContext *const vk_ctx_; + const QueryExtentCallback QueryExtent; + vk::UniqueCommandPool command_pool_; vk::Queue graphics_queue_; vk::Queue present_queue_; @@ -87,7 +92,7 @@ class Scene { std::unique_ptr swap_chain_context_; // Creates a new swapchain and returns the old one. - void CreateSwapChain(); + void CreateSwapChainContext(); uint32_t current_buffer_; diff --git a/space.cc b/space.cc index c1a31a0..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,12 @@ 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; @@ -148,6 +161,7 @@ int main(int argc, char *argv[]) { XSelectInput(display, window, ExposureMask | KeyPressMask + | StructureNotifyMask | PointerMotionMask); XMapWindow(display, window); XFlush(display); @@ -189,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());