diff options
Diffstat (limited to 'cube/cube.cpp')
| -rw-r--r-- | cube/cube.cpp | 524 |
1 files changed, 296 insertions, 228 deletions
diff --git a/cube/cube.cpp b/cube/cube.cpp index 7301482f..ec292436 100644 --- a/cube/cube.cpp +++ b/cube/cube.cpp @@ -315,27 +315,34 @@ const char *wsi_to_string(WsiPlatform wsi_platform) { } }; -struct SwapchainImageResources { - vk::Image image; +struct SubmissionResources { + vk::Fence fence; + vk::Semaphore image_acquired_semaphore; vk::CommandBuffer cmd; vk::CommandBuffer graphics_to_present_cmd; - vk::ImageView view; vk::Buffer uniform_buffer; vk::DeviceMemory uniform_memory; void *uniform_memory_ptr = nullptr; - vk::Framebuffer framebuffer; vk::DescriptorSet descriptor_set; }; +struct SwapchainImageResources { + vk::Image image; + vk::ImageView view; + vk::Framebuffer framebuffer; + vk::Semaphore draw_complete_semaphore; + vk::Semaphore image_ownership_semaphore; +}; + struct Demo { - void build_image_ownership_cmd(const SwapchainImageResources &swapchain_image_resource); + void build_image_ownership_cmd(const SubmissionResources &submission_resource, + const SwapchainImageResources &swapchain_image_resource); vk::Bool32 check_layers(const std::vector<const char *> &check_names, const std::vector<vk::LayerProperties> &layers); void cleanup(); - void destroy_swapchain_related_resources(); void create_device(); void destroy_texture(texture_object &tex_objs); void draw(); - void draw_build_cmd(const SwapchainImageResources &swapchain_image_resource); + void draw_build_cmd(const SubmissionResources &submission_resource, const SwapchainImageResources &swapchain_image_resource); void prepare_init_cmd(); void flush_init_cmd(); void init(int argc, char **argv); @@ -343,14 +350,13 @@ struct Demo { void init_vk(); void select_physical_device(); void init_vk_swapchain(); + void prepare(); - void prepare_buffers(); void prepare_cube_data_buffers(); - void prepare_depth(); void prepare_descriptor_layout(); void prepare_descriptor_pool(); void prepare_descriptor_set(); - void prepare_framebuffers(); + void prepare_submission_sync_objects(); vk::ShaderModule prepare_shader_module(const uint32_t *code, size_t size); vk::ShaderModule prepare_vs(); vk::ShaderModule prepare_fs(); @@ -363,9 +369,13 @@ struct Demo { void resize(); void create_surface(); + void prepare_swapchain(); + void prepare_framebuffers(); + void prepare_depth(); + void set_image_layout(vk::Image image, vk::ImageAspectFlags aspectMask, vk::ImageLayout oldLayout, vk::ImageLayout newLayout, vk::AccessFlags srcAccessMask, vk::PipelineStageFlags src_stages, vk::PipelineStageFlags dest_stages); - void update_data_buffer(); + void update_data_buffer(void *uniform_memory_ptr); bool loadTexture(const char *filename, uint8_t *rgba_data, vk::SubresourceLayout &layout, uint32_t &width, uint32_t &height); bool memory_type_from_properties(uint32_t typeBits, vk::MemoryPropertyFlags requirements_mask, uint32_t &typeIndex); vk::SurfaceFormatKHR pick_surface_format(const std::vector<vk::SurfaceFormatKHR> &surface_formats); @@ -461,7 +471,9 @@ struct Demo { #endif WsiPlatform wsi_platform = WsiPlatform::auto_; vk::SurfaceKHR surface; - bool prepared = false; + bool initialized; + bool swapchain_ready; + bool is_minimized; bool use_staging_buffer = false; bool separate_present_queue = false; bool invalid_gpu_selection = false; @@ -479,12 +491,11 @@ struct Demo { vk::Queue present_queue; uint32_t graphics_queue_family_index = 0; uint32_t present_queue_family_index = 0; - std::array<vk::Semaphore, FRAME_LAG> image_acquired_semaphores; - std::array<vk::Semaphore, FRAME_LAG> draw_complete_semaphores; - std::array<vk::Semaphore, FRAME_LAG> image_ownership_semaphores; vk::PhysicalDeviceProperties gpu_props; std::vector<vk::QueueFamilyProperties> queue_props; vk::PhysicalDeviceMemoryProperties memory_properties; + std::array<SubmissionResources, FRAME_LAG> submission_resources; + uint32_t current_submission_index = 0; std::vector<const char *> enabled_instance_extensions; std::vector<const char *> enabled_layers; @@ -496,10 +507,8 @@ struct Demo { vk::ColorSpaceKHR color_space; vk::SwapchainKHR swapchain; - std::vector<SwapchainImageResources> swapchain_image_resources; + std::vector<SwapchainImageResources> swapchain_resources; vk::PresentModeKHR presentMode = vk::PresentModeKHR::eFifo; - std::array<vk::Fence, FRAME_LAG> fences; - uint32_t frame_index = 0; bool first_swapchain_frame; vk::CommandPool cmd_pool; @@ -556,9 +565,6 @@ struct Demo { bool use_break = false; bool suppress_popups = false; bool force_errors = false; - bool is_minimized = false; - - uint32_t current_buffer = 0; }; #ifdef _WIN32 @@ -672,8 +678,12 @@ static void registry_handle_global_remove(void *data, wl_registry *registry, uin static const wl_registry_listener registry_listener = {registry_handle_global, registry_handle_global_remove}; #endif -void Demo::build_image_ownership_cmd(const SwapchainImageResources &swapchain_image_resource) { - auto result = swapchain_image_resource.graphics_to_present_cmd.begin( +void Demo::build_image_ownership_cmd(const SubmissionResources &submission_resource, + const SwapchainImageResources &swapchain_image_resource) { + auto result = submission_resource.graphics_to_present_cmd.reset(); + VERIFY(result == vk::Result::eSuccess); + + result = submission_resource.graphics_to_present_cmd.begin( vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eSimultaneousUse)); VERIFY(result == vk::Result::eSuccess); @@ -688,11 +698,11 @@ void Demo::build_image_ownership_cmd(const SwapchainImageResources &swapchain_im .setImage(swapchain_image_resource.image) .setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); - swapchain_image_resource.graphics_to_present_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eBottomOfPipe, - vk::PipelineStageFlagBits::eBottomOfPipe, - vk::DependencyFlagBits(), {}, {}, image_ownership_barrier); + submission_resource.graphics_to_present_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eBottomOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, vk::DependencyFlagBits(), + {}, {}, image_ownership_barrier); - result = swapchain_image_resource.graphics_to_present_cmd.end(); + result = submission_resource.graphics_to_present_cmd.end(); VERIFY(result == vk::Result::eSuccess); } @@ -714,24 +724,53 @@ vk::Bool32 Demo::check_layers(const std::vector<const char *> &check_names, cons } void Demo::cleanup() { - prepared = false; + initialized = false; auto result = device.waitIdle(); VERIFY(result == vk::Result::eSuccess); - if (!is_minimized) { - destroy_swapchain_related_resources(); + + device.destroyCommandPool(cmd_pool); + if (separate_present_queue) { + device.destroyCommandPool(present_cmd_pool); + } + + device.destroyDescriptorPool(desc_pool); + + device.destroyPipeline(pipeline); + device.destroyPipelineCache(pipelineCache); + device.destroyRenderPass(render_pass); + device.destroyPipelineLayout(pipeline_layout); + device.destroyDescriptorSetLayout(desc_layout); + + for (const auto &tex : textures) { + device.destroyImageView(tex.view); + device.destroyImage(tex.image); + device.freeMemory(tex.mem); + device.destroySampler(tex.sampler); } - // Wait for fences from present operations - for (uint32_t i = 0; i < FRAME_LAG; i++) { - device.destroyFence(fences[i]); - device.destroySemaphore(image_acquired_semaphores[i]); - device.destroySemaphore(draw_complete_semaphores[i]); + + device.destroyImageView(depth.view); + device.destroyImage(depth.image); + device.freeMemory(depth.mem); + + for (auto &swapchain_resource : swapchain_resources) { + device.destroyFramebuffer(swapchain_resource.framebuffer); + device.destroyImageView(swapchain_resource.view); + device.destroySemaphore(swapchain_resource.draw_complete_semaphore); if (separate_present_queue) { - device.destroySemaphore(image_ownership_semaphores[i]); + device.destroySemaphore(swapchain_resource.image_ownership_semaphore); } } device.destroySwapchainKHR(swapchain); + for (const auto &submission_resource : submission_resources) { + device.destroyFence(submission_resource.fence); + device.destroySemaphore(submission_resource.image_acquired_semaphore); + device.destroyBuffer(submission_resource.uniform_buffer); + device.unmapMemory(submission_resource.uniform_memory); + device.freeMemory(submission_resource.uniform_memory); + } + device.destroy(); inst.destroySurfaceKHR(surface); @@ -788,8 +827,12 @@ void Demo::create_device() { float priorities = 0.0; std::vector<vk::DeviceQueueCreateInfo> queues; - const vk::DeviceQueueCreateFlags queue_create_flags = protected_output ? vk::DeviceQueueCreateFlagBits::eProtected : vk::DeviceQueueCreateFlags{}; - queues.push_back(vk::DeviceQueueCreateInfo().setQueueFamilyIndex(graphics_queue_family_index).setQueuePriorities(priorities).setFlags(queue_create_flags)); + const vk::DeviceQueueCreateFlags queue_create_flags = + protected_output ? vk::DeviceQueueCreateFlagBits::eProtected : vk::DeviceQueueCreateFlags{}; + queues.push_back(vk::DeviceQueueCreateInfo() + .setQueueFamilyIndex(graphics_queue_family_index) + .setQueuePriorities(priorities) + .setFlags(queue_create_flags)); if (separate_present_queue) { queues.push_back( @@ -797,7 +840,10 @@ void Demo::create_device() { } auto const protected_memory_features = vk::PhysicalDeviceProtectedMemoryFeatures().setProtectedMemory(protected_output); - auto deviceInfo = vk::DeviceCreateInfo().setPNext(protected_output ? &protected_memory_features : nullptr).setQueueCreateInfos(queues).setPEnabledExtensionNames(enabled_device_extensions); + auto deviceInfo = vk::DeviceCreateInfo() + .setPNext(protected_output ? &protected_memory_features : nullptr) + .setQueueCreateInfos(queues) + .setPEnabledExtensionNames(enabled_device_extensions); auto device_return = gpu.createDevice(deviceInfo); VERIFY(device_return.result == vk::Result::eSuccess); device = device_return.value; @@ -812,15 +858,22 @@ void Demo::destroy_texture(texture_object &tex_objs) { } void Demo::draw() { + // Don't draw if initialization isn't complete, if the swapchain became outdated, or if the window is minimized + if (!initialized || !swapchain_ready || is_minimized) { + return; + } + + auto ¤t_submission = submission_resources[current_submission_index]; + // Ensure no more than FRAME_LAG renderings are outstanding - const vk::Result wait_result = device.waitForFences(fences[frame_index], VK_TRUE, UINT64_MAX); + const vk::Result wait_result = device.waitForFences(current_submission.fence, VK_TRUE, UINT64_MAX); VERIFY(wait_result == vk::Result::eSuccess || wait_result == vk::Result::eTimeout); - device.resetFences({fences[frame_index]}); vk::Result acquire_result; + uint32_t current_swapchain_image_index = 0; do { - acquire_result = - device.acquireNextImageKHR(swapchain, UINT64_MAX, image_acquired_semaphores[frame_index], vk::Fence(), ¤t_buffer); + acquire_result = device.acquireNextImageKHR(swapchain, UINT64_MAX, current_submission.image_acquired_semaphore, vk::Fence(), + ¤t_swapchain_image_index); if (acquire_result == vk::Result::eErrorOutOfDateKHR) { // demo.swapchain is out of date (e.g. the window was resized) and // must be recreated: @@ -836,9 +889,24 @@ void Demo::draw() { } else { VERIFY(acquire_result == vk::Result::eSuccess); } + // If we minimized then stop trying to draw + if (!swapchain_ready) { + return; + } } while (acquire_result != vk::Result::eSuccess); - update_data_buffer(); + auto ¤t_swapchain_resource = swapchain_resources[current_swapchain_image_index]; + + update_data_buffer(submission_resources[current_submission_index].uniform_memory_ptr); + + draw_build_cmd(current_submission, current_swapchain_resource); + + if (separate_present_queue) { + build_image_ownership_cmd(current_submission, current_swapchain_resource); + } + + // Only reset right before submitting so we can't deadlock on an un-signalled fence that has nothing submitted to it + device.resetFences({current_submission.fence}); // Wait for the image acquired semaphore to be signaled to ensure // that the image won't be rendered to until the presentation @@ -850,10 +918,10 @@ void Demo::draw() { auto submit_result = graphics_queue.submit(vk::SubmitInfo() .setPNext(protected_output ? &protected_submit_info : nullptr) .setWaitDstStageMask(pipe_stage_flags) - .setWaitSemaphores(image_acquired_semaphores[frame_index]) - .setCommandBuffers(swapchain_image_resources[current_buffer].cmd) - .setSignalSemaphores(draw_complete_semaphores[frame_index]), - fences[frame_index]); + .setWaitSemaphores(current_submission.image_acquired_semaphore) + .setCommandBuffers(current_submission.cmd) + .setSignalSemaphores(current_swapchain_resource.draw_complete_semaphore), + current_submission.fence); VERIFY(submit_result == vk::Result::eSuccess); if (separate_present_queue) { @@ -864,23 +932,23 @@ void Demo::draw() { auto change_owner_result = present_queue.submit(vk::SubmitInfo() .setWaitDstStageMask(pipe_stage_flags) - .setWaitSemaphores(draw_complete_semaphores[frame_index]) - .setCommandBuffers(swapchain_image_resources[current_buffer].graphics_to_present_cmd) - .setSignalSemaphores(image_ownership_semaphores[frame_index])); + .setWaitSemaphores(current_swapchain_resource.draw_complete_semaphore) + .setCommandBuffers(current_submission.graphics_to_present_cmd) + .setSignalSemaphores(current_swapchain_resource.image_ownership_semaphore)); VERIFY(change_owner_result == vk::Result::eSuccess); } const auto presentInfo = vk::PresentInfoKHR() - .setWaitSemaphores(separate_present_queue ? image_ownership_semaphores[frame_index] - : draw_complete_semaphores[frame_index]) + .setWaitSemaphores(separate_present_queue ? current_swapchain_resource.image_ownership_semaphore + : current_swapchain_resource.draw_complete_semaphore) .setSwapchains(swapchain) - .setImageIndices(current_buffer); + .setImageIndices(current_swapchain_image_index); // If we are using separate queues we have to wait for image ownership, // otherwise wait for draw complete auto present_result = present_queue.presentKHR(&presentInfo); - frame_index += 1; - frame_index %= FRAME_LAG; + current_submission_index += 1; + current_submission_index %= FRAME_LAG; first_swapchain_frame = false; if (present_result == vk::Result::eErrorOutOfDateKHR) { // swapchain is out of date (e.g. the window was resized) and @@ -892,6 +960,8 @@ void Demo::draw() { auto caps_result = gpu.getSurfaceCapabilitiesKHR(surface, &surfCapabilities); VERIFY(caps_result == vk::Result::eSuccess); if (surfCapabilities.currentExtent.width != width || surfCapabilities.currentExtent.height != height) { + width = surfCapabilities.currentExtent.width; + height = surfCapabilities.currentExtent.height; resize(); } } else if (present_result == vk::Result::eErrorSurfaceLostKHR) { @@ -903,12 +973,14 @@ void Demo::draw() { } } -void Demo::draw_build_cmd(const SwapchainImageResources &swapchain_image_resource) { - const auto commandBuffer = swapchain_image_resource.cmd; +void Demo::draw_build_cmd(const SubmissionResources &submission_resource, const SwapchainImageResources &swapchain_image_resource) { + const auto commandBuffer = submission_resource.cmd; vk::ClearValue const clearValues[2] = {vk::ClearColorValue(std::array<float, 4>({{0.2f, 0.2f, 0.2f, 0.2f}})), vk::ClearDepthStencilValue(1.0f, 0u)}; + auto result = commandBuffer.reset(); + VERIFY(result == vk::Result::eSuccess); - auto result = commandBuffer.begin(vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eSimultaneousUse)); + result = commandBuffer.begin(vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eSimultaneousUse)); VERIFY(result == vk::Result::eSuccess); commandBuffer.beginRenderPass(vk::RenderPassBeginInfo() @@ -920,8 +992,7 @@ void Demo::draw_build_cmd(const SwapchainImageResources &swapchain_image_resourc vk::SubpassContents::eInline); commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, swapchain_image_resource.descriptor_set, - {}); + commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, submission_resource.descriptor_set, {}); float viewport_dimension; float viewport_x = 0.0f; float viewport_y = 0.0f; @@ -977,6 +1048,7 @@ void Demo::draw_build_cmd(const SwapchainImageResources &swapchain_image_resourc void Demo::prepare_init_cmd() { vk::CommandPoolCreateFlags cmd_pool_create_flags = protected_output ? vk::CommandPoolCreateFlagBits::eProtected : vk::CommandPoolCreateFlags{}; + cmd_pool_create_flags |= vk::CommandPoolCreateFlagBits::eResetCommandBuffer; auto cmd_pool_return = device.createCommandPool( vk::CommandPoolCreateInfo().setQueueFamilyIndex(graphics_queue_family_index).setFlags(cmd_pool_create_flags)); VERIFY(cmd_pool_return.result == vk::Result::eSuccess); @@ -1007,7 +1079,8 @@ void Demo::flush_init_cmd() { auto fence = fence_return.value; auto const protected_submit_info = vk::ProtectedSubmitInfo().setProtectedSubmit(protected_output); - result = graphics_queue.submit(vk::SubmitInfo().setPNext(protected_output ? &protected_submit_info : nullptr).setCommandBuffers(cmd), fence); + result = graphics_queue.submit( + vk::SubmitInfo().setPNext(protected_output ? &protected_submit_info : nullptr).setCommandBuffers(cmd), fence); VERIFY(result == vk::Result::eSuccess); result = device.waitForFences(fence, VK_TRUE, UINT64_MAX); @@ -1185,6 +1258,8 @@ void Demo::init(int argc, char **argv) { exit(1); } + initialized = false; + init_vk(); spin_angle = 4.0f; @@ -1834,21 +1909,17 @@ void Demo::select_physical_device() { assert(physicalDeviceProperties.deviceType <= vk::PhysicalDeviceType::eCpu); auto support_result = physical_devices[i].getSurfaceSupportKHR(0, surface); - if (support_result.result != vk::Result::eSuccess || - support_result.value != vk::True) { + if (support_result.result != vk::Result::eSuccess || support_result.value != vk::True) { continue; } std::map<vk::PhysicalDeviceType, int> device_type_priorities = { - {vk::PhysicalDeviceType::eDiscreteGpu, 5}, - {vk::PhysicalDeviceType::eIntegratedGpu, 4}, - {vk::PhysicalDeviceType::eVirtualGpu, 3}, - {vk::PhysicalDeviceType::eCpu, 2}, + {vk::PhysicalDeviceType::eDiscreteGpu, 5}, {vk::PhysicalDeviceType::eIntegratedGpu, 4}, + {vk::PhysicalDeviceType::eVirtualGpu, 3}, {vk::PhysicalDeviceType::eCpu, 2}, {vk::PhysicalDeviceType::eOther, 1}, }; int priority = -1; - if (device_type_priorities.find(physicalDeviceProperties.deviceType) != - device_type_priorities.end()) { + if (device_type_priorities.find(physicalDeviceProperties.deviceType) != device_type_priorities.end()) { priority = device_type_priorities[physicalDeviceProperties.deviceType]; } @@ -2081,56 +2152,28 @@ void Demo::init_vk_swapchain() { first_swapchain_frame = true; curFrame = 0; - // Create semaphores to synchronize acquiring presentable buffers before - // rendering and waiting for drawing to be complete before presenting - auto const semaphoreCreateInfo = vk::SemaphoreCreateInfo(); - - // Create fences that we can use to throttle if we get too far - // ahead of the image presents - auto const fence_ci = vk::FenceCreateInfo().setFlags(vk::FenceCreateFlagBits::eSignaled); - for (uint32_t i = 0; i < FRAME_LAG; i++) { - vk::Result result = device.createFence(&fence_ci, nullptr, &fences[i]); - VERIFY(result == vk::Result::eSuccess); - - result = device.createSemaphore(&semaphoreCreateInfo, nullptr, &image_acquired_semaphores[i]); - VERIFY(result == vk::Result::eSuccess); - - result = device.createSemaphore(&semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]); - VERIFY(result == vk::Result::eSuccess); - - if (separate_present_queue) { - result = device.createSemaphore(&semaphoreCreateInfo, nullptr, &image_ownership_semaphores[i]); - VERIFY(result == vk::Result::eSuccess); - } - } - frame_index = 0; - // Get Memory information and properties memory_properties = gpu.getMemoryProperties(); } void Demo::prepare() { - prepare_buffers(); - if (is_minimized) { - prepared = false; - return; - } prepare_init_cmd(); - prepare_depth(); prepare_textures(); prepare_cube_data_buffers(); prepare_descriptor_layout(); + // Only need to know the format of the depth buffer before we create the renderpass + depth.format = vk::Format::eD16Unorm; prepare_render_pass(); prepare_pipeline(); - for (auto &swapchain_image_resource : swapchain_image_resources) { + for (auto &submission_resource : submission_resources) { auto alloc_return = device.allocateCommandBuffers(vk::CommandBufferAllocateInfo() .setCommandPool(cmd_pool) .setLevel(vk::CommandBufferLevel::ePrimary) .setCommandBufferCount(1)); VERIFY(alloc_return.result == vk::Result::eSuccess); - swapchain_image_resource.cmd = alloc_return.value[0]; + submission_resource.cmd = alloc_return.value[0]; } if (separate_present_queue) { @@ -2139,14 +2182,13 @@ void Demo::prepare() { VERIFY(present_cmd_pool_return.result == vk::Result::eSuccess); present_cmd_pool = present_cmd_pool_return.value; - for (auto &swapchain_image_resource : swapchain_image_resources) { + for (auto &submission_resource : submission_resources) { auto alloc_cmd_return = device.allocateCommandBuffers(vk::CommandBufferAllocateInfo() .setCommandPool(present_cmd_pool) .setLevel(vk::CommandBufferLevel::ePrimary) .setCommandBufferCount(1)); VERIFY(alloc_cmd_return.result == vk::Result::eSuccess); - swapchain_image_resource.graphics_to_present_cmd = alloc_cmd_return.value[0]; - build_image_ownership_cmd(swapchain_image_resource); + submission_resource.graphics_to_present_cmd = alloc_cmd_return.value[0]; } } @@ -2155,9 +2197,7 @@ void Demo::prepare() { prepare_framebuffers(); - for (const auto &swapchain_image_resource : swapchain_image_resources) { - draw_build_cmd(swapchain_image_resource); - } + prepare_submission_sync_objects(); /* * Prepare functions above may generate pipeline commands @@ -2168,12 +2208,14 @@ void Demo::prepare() { destroy_texture(staging_texture); } - current_buffer = 0; - prepared = true; - first_swapchain_frame = true; + initialized = true; + + prepare_swapchain(); } -void Demo::prepare_buffers() { +// Creates the swapchain, swapchain image views, depth buffer, framebuffers, and sempahores. +// This function returns early if it fails to create a swapchain, setting swapchain_ready to false. +void Demo::prepare_swapchain() { vk::SwapchainKHR oldSwapchain = swapchain; // Check the surface capabilities and formats @@ -2323,22 +2365,41 @@ void Demo::prepare_buffers() { auto swapchain_images_return = device.getSwapchainImagesKHR(swapchain); VERIFY(swapchain_images_return.result == vk::Result::eSuccess); - swapchain_image_resources.resize(swapchain_images_return.value.size()); + swapchain_resources.resize(swapchain_images_return.value.size()); - for (uint32_t i = 0; i < swapchain_image_resources.size(); ++i) { + for (uint32_t i = 0; i < swapchain_resources.size(); ++i) { auto color_image_view = vk::ImageViewCreateInfo() .setViewType(vk::ImageViewType::e2D) .setFormat(format) .setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); - swapchain_image_resources[i].image = swapchain_images_return.value[i]; + swapchain_resources[i].image = swapchain_images_return.value[i]; - color_image_view.image = swapchain_image_resources[i].image; + color_image_view.image = swapchain_resources[i].image; auto image_view_return = device.createImageView(color_image_view); VERIFY(image_view_return.result == vk::Result::eSuccess); - swapchain_image_resources[i].view = image_view_return.value; + swapchain_resources[i].view = image_view_return.value; + } + + // Create semaphores to synchronize acquiring presentable buffers before + // rendering and waiting for drawing to be complete before presenting + auto const semaphoreCreateInfo = vk::SemaphoreCreateInfo(); + + for (auto &swapchain_resource : swapchain_resources) { + auto result = device.createSemaphore(&semaphoreCreateInfo, nullptr, &swapchain_resource.draw_complete_semaphore); + VERIFY(result == vk::Result::eSuccess); + + if (separate_present_queue) { + result = device.createSemaphore(&semaphoreCreateInfo, nullptr, &swapchain_resource.image_ownership_semaphore); + VERIFY(result == vk::Result::eSuccess); + } } + + prepare_depth(); + prepare_framebuffers(); + swapchain_ready = true; + first_swapchain_frame = true; } void Demo::prepare_cube_data_buffers() { @@ -2365,12 +2426,12 @@ void Demo::prepare_cube_data_buffers() { auto const buf_info = vk::BufferCreateInfo().setSize(sizeof(data)).setUsage(vk::BufferUsageFlagBits::eUniformBuffer); - for (auto &swapchain_image_resource : swapchain_image_resources) { - auto result = device.createBuffer(&buf_info, nullptr, &swapchain_image_resource.uniform_buffer); + for (auto &submission_resource : submission_resources) { + auto result = device.createBuffer(&buf_info, nullptr, &submission_resource.uniform_buffer); VERIFY(result == vk::Result::eSuccess); vk::MemoryRequirements mem_reqs; - device.getBufferMemoryRequirements(swapchain_image_resource.uniform_buffer, &mem_reqs); + device.getBufferMemoryRequirements(submission_resource.uniform_buffer, &mem_reqs); auto mem_alloc = vk::MemoryAllocateInfo().setAllocationSize(mem_reqs.size).setMemoryTypeIndex(0); @@ -2379,16 +2440,16 @@ void Demo::prepare_cube_data_buffers() { mem_alloc.memoryTypeIndex); VERIFY(pass); - result = device.allocateMemory(&mem_alloc, nullptr, &swapchain_image_resource.uniform_memory); + result = device.allocateMemory(&mem_alloc, nullptr, &submission_resource.uniform_memory); VERIFY(result == vk::Result::eSuccess); - result = device.mapMemory(swapchain_image_resource.uniform_memory, 0, VK_WHOLE_SIZE, vk::MemoryMapFlags(), - &swapchain_image_resource.uniform_memory_ptr); + result = device.mapMemory(submission_resource.uniform_memory, 0, VK_WHOLE_SIZE, vk::MemoryMapFlags(), + &submission_resource.uniform_memory_ptr); VERIFY(result == vk::Result::eSuccess); - memcpy(swapchain_image_resource.uniform_memory_ptr, &data, sizeof data); + memcpy(submission_resource.uniform_memory_ptr, &data, sizeof data); - result = device.bindBufferMemory(swapchain_image_resource.uniform_buffer, swapchain_image_resource.uniform_memory, 0); + result = device.bindBufferMemory(submission_resource.uniform_buffer, submission_resource.uniform_memory, 0); VERIFY(result == vk::Result::eSuccess); } } @@ -2470,13 +2531,13 @@ void Demo::prepare_descriptor_pool() { std::array<vk::DescriptorPoolSize, 2> const poolSizes = { vk::DescriptorPoolSize() .setType(vk::DescriptorType::eUniformBuffer) - .setDescriptorCount(static_cast<uint32_t>(swapchain_image_resources.size())), + .setDescriptorCount(static_cast<uint32_t>(submission_resources.size())), vk::DescriptorPoolSize() .setType(vk::DescriptorType::eCombinedImageSampler) - .setDescriptorCount(static_cast<uint32_t>(swapchain_image_resources.size()) * texture_count)}; + .setDescriptorCount(static_cast<uint32_t>(submission_resources.size()) * texture_count)}; auto const descriptor_pool = - vk::DescriptorPoolCreateInfo().setMaxSets(static_cast<uint32_t>(swapchain_image_resources.size())).setPoolSizes(poolSizes); + vk::DescriptorPoolCreateInfo().setMaxSets(static_cast<uint32_t>(submission_resources.size())).setPoolSizes(poolSizes); auto result = device.createDescriptorPool(&descriptor_pool, nullptr, &desc_pool); VERIFY(result == vk::Result::eSuccess); @@ -2502,13 +2563,13 @@ void Demo::prepare_descriptor_set() { .setDescriptorType(vk::DescriptorType::eCombinedImageSampler) .setImageInfo(tex_descs); - for (auto &swapchain_image_resource : swapchain_image_resources) { - auto result = device.allocateDescriptorSets(&alloc_info, &swapchain_image_resource.descriptor_set); + for (auto &submission_resource : submission_resources) { + auto result = device.allocateDescriptorSets(&alloc_info, &submission_resource.descriptor_set); VERIFY(result == vk::Result::eSuccess); - buffer_info.setBuffer(swapchain_image_resource.uniform_buffer); - writes[0].setDstSet(swapchain_image_resource.descriptor_set); - writes[1].setDstSet(swapchain_image_resource.descriptor_set); + buffer_info.setBuffer(submission_resource.uniform_buffer); + writes[0].setDstSet(submission_resource.descriptor_set); + writes[1].setDstSet(submission_resource.descriptor_set); device.updateDescriptorSets(writes, {}); } } @@ -2517,7 +2578,7 @@ void Demo::prepare_framebuffers() { std::array<vk::ImageView, 2> attachments; attachments[1] = depth.view; - for (auto &swapchain_image_resource : swapchain_image_resources) { + for (auto &swapchain_image_resource : swapchain_resources) { attachments[0] = swapchain_image_resource.view; auto const framebuffer_return = device.createFramebuffer(vk::FramebufferCreateInfo() .setRenderPass(render_pass) @@ -2530,6 +2591,23 @@ void Demo::prepare_framebuffers() { } } +void Demo::prepare_submission_sync_objects() { + // Create semaphores to synchronize acquiring presentable buffers before + // rendering and waiting for drawing to be complete before presenting + auto const semaphoreCreateInfo = vk::SemaphoreCreateInfo(); + + // Create fences that we can use to throttle if we get too far + // ahead of the image presents + auto const fence_ci = vk::FenceCreateInfo().setFlags(vk::FenceCreateFlagBits::eSignaled); + for (auto &submission_resource : submission_resources) { + vk::Result result = device.createFence(&fence_ci, nullptr, &submission_resource.fence); + VERIFY(result == vk::Result::eSuccess); + + result = device.createSemaphore(&semaphoreCreateInfo, nullptr, &submission_resource.image_acquired_semaphore); + VERIFY(result == vk::Result::eSuccess); + } +} + vk::ShaderModule Demo::prepare_fs() { const uint32_t fragShaderCode[] = { #ifdef CUBE_FRAG_INC @@ -2879,63 +2957,47 @@ vk::ShaderModule Demo::prepare_vs() { return vert_shader_module; } -void Demo::destroy_swapchain_related_resources() { - device.destroyDescriptorPool(desc_pool); - - device.destroyPipeline(pipeline); - device.destroyPipelineCache(pipelineCache); - device.destroyRenderPass(render_pass); - device.destroyPipelineLayout(pipeline_layout); - device.destroyDescriptorSetLayout(desc_layout); - - for (const auto &tex : textures) { - device.destroyImageView(tex.view); - device.destroyImage(tex.image); - device.freeMemory(tex.mem); - device.destroySampler(tex.sampler); - } - - device.destroyImageView(depth.view); - device.destroyImage(depth.image); - device.freeMemory(depth.mem); - - for (const auto &resource : swapchain_image_resources) { - device.destroyFramebuffer(resource.framebuffer); - device.destroyImageView(resource.view); - device.freeCommandBuffers(cmd_pool, {resource.cmd}); - device.destroyBuffer(resource.uniform_buffer); - device.unmapMemory(resource.uniform_memory); - device.freeMemory(resource.uniform_memory); - } - - device.destroyCommandPool(cmd_pool); - if (separate_present_queue) { - device.destroyCommandPool(present_cmd_pool); - } -} - void Demo::resize() { // Don't react to resize until after first initialization. - if (!prepared) { - if (is_minimized) { - prepare(); - } + if (!initialized) { return; } + // Don't do anything if the surface has zero size, as vulkan disallows creating swapchains with zero area + // We use is_minimized to track this because zero size window usually occurs from minimizing + if (width == 0 || height == 0) { + is_minimized = true; + return; + } else { + is_minimized = false; + } + // In order to properly resize the window, we must re-create the // swapchain - // AND redo the command buffers, etc. // - // First, perform part of the cleanup() function: - prepared = false; - auto result = device.waitIdle(); - VERIFY(result == vk::Result::eSuccess); - destroy_swapchain_related_resources(); + // First, destroy the old swapchain and its associated resources, setting swapchain_ready to false to prevent draw from running + if (swapchain_ready) { + swapchain_ready = false; + auto result = device.waitIdle(); + VERIFY(result == vk::Result::eSuccess); - // Second, re-perform the prepare() function, which will re-create the - // swapchain. - prepare(); + device.destroyImageView(depth.view); + device.destroyImage(depth.image); + device.freeMemory(depth.mem); + depth = {}; + + for (auto &swapchain_resource : swapchain_resources) { + device.destroyFramebuffer(swapchain_resource.framebuffer); + device.destroyImageView(swapchain_resource.view); + device.destroySemaphore(swapchain_resource.draw_complete_semaphore); + if (separate_present_queue) { + device.destroySemaphore(swapchain_resource.image_ownership_semaphore); + } + } + swapchain_resources.clear(); + } + // Second, recreate the swapchain, depth buffer, and framebuffers. + prepare_swapchain(); } void Demo::set_image_layout(vk::Image image, vk::ImageAspectFlags aspectMask, vk::ImageLayout oldLayout, vk::ImageLayout newLayout, @@ -2986,7 +3048,7 @@ void Demo::set_image_layout(vk::Image image, vk::ImageAspectFlags aspectMask, vk .setSubresourceRange(vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1))); } -void Demo::update_data_buffer() { +void Demo::update_data_buffer(void *uniform_memory_ptr) { mat4x4 VP; mat4x4_mul(VP, projection_matrix, view_matrix); @@ -2999,7 +3061,7 @@ void Demo::update_data_buffer() { mat4x4 MVP; mat4x4_mul(MVP, VP, model_matrix); - memcpy(swapchain_image_resources[current_buffer].uniform_memory_ptr, (const void *)&MVP[0][0], sizeof(MVP)); + memcpy(uniform_memory_ptr, (const void *)&MVP[0][0], sizeof(MVP)); } /* Convert ppm image data from header file into RGBA texture image */ @@ -3015,19 +3077,16 @@ bool Demo::loadTexture(const char *filename, uint8_t *rgba_data, vk::Subresource if ((unsigned char *)cPtr >= (lunarg_ppm + lunarg_ppm_len) || strncmp(cPtr, "P6\n", 3)) { return false; } - while (strncmp(cPtr++, "\n", 1)) - ; + while (strncmp(cPtr++, "\n", 1)); sscanf(cPtr, "%u %u", &width, &height); if (rgba_data == nullptr) { return true; } - while (strncmp(cPtr++, "\n", 1)) - ; + while (strncmp(cPtr++, "\n", 1)); if ((unsigned char *)cPtr >= (lunarg_ppm + lunarg_ppm_len) || strncmp(cPtr, "255\n", 4)) { return false; } - while (strncmp(cPtr++, "\n", 1)) - ; + while (strncmp(cPtr++, "\n", 1)); for (uint32_t y = 0; y < height; y++) { uint8_t *rowPtr = rgba_data; for (uint32_t x = 0; x < width; x++) { @@ -3080,7 +3139,7 @@ vk::SurfaceFormatKHR Demo::pick_surface_format(const std::vector<vk::SurfaceForm #if defined(VK_USE_PLATFORM_WIN32_KHR) template <> void Demo::run<WsiPlatform::win32>() { - if (!prepared) { + if (!initialized || !swapchain_ready) { return; } @@ -3235,14 +3294,15 @@ void Demo::run<WsiPlatform::xlib>() { XNextEvent(xlib_display, &event); handle_xlib_event(&event); } + if (initialized && swapchain_ready) { + draw(); + if (!is_minimized) { + curFrame++; + } - draw(); - if (!is_minimized) { - curFrame++; - } - - if (frameCount != UINT32_MAX && curFrame == frameCount) { - quit = true; + if (frameCount != UINT32_MAX && curFrame == frameCount) { + quit = true; + } } } } @@ -3308,13 +3368,14 @@ void Demo::run<WsiPlatform::xcb>() { free(event); event = xcb_poll_for_event(connection); } - - draw(); - if (!is_minimized) { - curFrame++; - } - if (frameCount != UINT32_MAX && curFrame == frameCount) { - quit = true; + if (initialized && swapchain_ready) { + draw(); + if (!is_minimized) { + curFrame++; + } + if (frameCount != UINT32_MAX && curFrame == frameCount) { + quit = true; + } } } } @@ -3361,12 +3422,14 @@ void Demo::run<WsiPlatform::wayland>() { wl_display_dispatch(wayland_display); } else { wl_display_dispatch_pending(wayland_display); - draw(); - if (!is_minimized) { - curFrame++; - } - if (frameCount != UINT32_MAX && curFrame == frameCount) { - quit = true; + if (initialized && swapchain_ready) { + draw(); + if (!is_minimized) { + curFrame++; + } + if (frameCount != UINT32_MAX && curFrame == frameCount) { + quit = true; + } } } } @@ -3475,13 +3538,14 @@ void Demo::run<WsiPlatform::directfb>() { if (!event_buffer->GetEvent(event_buffer, DFB_EVENT(&event))) handle_directfb_event(&event); } else { if (!event_buffer->GetEvent(event_buffer, DFB_EVENT(&event))) handle_directfb_event(&event); - - draw(); - if (!is_minimized) { - curFrame++; - } - if (frameCount != UINT32_MAX && curFrame == frameCount) { - quit = true; + if (initialized && swapchain_ready) { + draw(); + if (!is_minimized) { + curFrame++; + } + if (frameCount != UINT32_MAX && curFrame == frameCount) { + quit = true; + } } } } @@ -3528,6 +3592,9 @@ void Demo::create_window<WsiPlatform::directfb>() { #if defined(VK_USE_PLATFORM_METAL_EXT) template <> void Demo::run<WsiPlatform::metal>() { + if (!initialized || !swapchain_ready) { + return; + } draw(); if (!is_minimized) { curFrame++; @@ -3742,7 +3809,7 @@ void Demo::run<WsiPlatform::qnx>() { } } - if (pause) { + if (pause || !initialized || !swapchain_ready) { } else { update_data_buffer(); draw(); @@ -3850,7 +3917,7 @@ void Demo::create_window<WsiPlatform::fuchsia_scenic>() { auto resize_callback = [this](uint32_t width, uint32_t height) { this->width = width; this->height = height; - if (prepared) { + if (initialized) { resize(); } }; @@ -3915,16 +3982,17 @@ void Demo::run<WsiPlatform::fuchsia_display>() { num_frames = static_cast<uint32_t>(fps); elapsed_frames = 0; } + if (initialized && swapchain_ready) { + draw(); - draw(); - - if (!is_minimized) { - curFrame++; - } - elapsed_frames++; + if (!is_minimized) { + curFrame++; + } + elapsed_frames++; - if (frameCount != UINT32_MAX && curFrame == frameCount) { - quit = true; + if (frameCount != UINT32_MAX && curFrame == frameCount) { + quit = true; + } } } } |
