diff options
author | Rémi Verschelde <rverschelde@gmail.com> | 2024-02-22 11:23:19 +0100 |
---|---|---|
committer | Rémi Verschelde <rverschelde@gmail.com> | 2024-02-22 11:23:19 +0100 |
commit | d646b7fef63906e09ff16ddae0e827e0cd50a063 (patch) | |
tree | 54d3acd873aa1c741e2b89ed2d6107f367a92a4e /drivers | |
parent | 0a3f162f17bbe1c63f8691131bba84017ba6c557 (diff) | |
parent | ee2d8f68baa752612a76c3351e85aa2b72ab32df (diff) | |
download | redot-engine-d646b7fef63906e09ff16ddae0e827e0cd50a063.tar.gz |
Merge pull request #88560 from DarioSamo/rdd_execute_and_present
Merge execute and present commands for RenderingDeviceDriver.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/d3d12/rendering_device_driver_d3d12.cpp | 46 | ||||
-rw-r--r-- | drivers/d3d12/rendering_device_driver_d3d12.h | 3 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_driver_vulkan.cpp | 223 | ||||
-rw-r--r-- | drivers/vulkan/rendering_device_driver_vulkan.h | 6 |
4 files changed, 151 insertions, 127 deletions
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 6517b4e91b..8c92737374 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -1955,48 +1955,44 @@ RDD::CommandQueueID RenderingDeviceDriverD3D12::command_queue_create(CommandQueu return CommandQueueID(command_queue); } -Error RenderingDeviceDriverD3D12::command_queue_execute(CommandQueueID p_cmd_queue, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_wait_semaphores, VectorView<SemaphoreID> p_signal_semaphores, FenceID p_signal_fence) { +Error RenderingDeviceDriverD3D12::command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) { CommandQueueInfo *command_queue = (CommandQueueInfo *)(p_cmd_queue.id); for (uint32_t i = 0; i < p_wait_semaphores.size(); i++) { const SemaphoreInfo *semaphore = (const SemaphoreInfo *)(p_wait_semaphores[i].id); command_queue->d3d_queue->Wait(semaphore->d3d_fence.Get(), semaphore->fence_value); } - thread_local LocalVector<ID3D12CommandList *> command_lists; - command_lists.resize(p_cmd_buffers.size()); - for (uint32_t i = 0; i < p_cmd_buffers.size(); i++) { - const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)(p_cmd_buffers[i].id); - command_lists[i] = cmd_buf_info->cmd_list.Get(); - } + if (p_cmd_buffers.size() > 0) { + thread_local LocalVector<ID3D12CommandList *> command_lists; + command_lists.resize(p_cmd_buffers.size()); + for (uint32_t i = 0; i < p_cmd_buffers.size(); i++) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)(p_cmd_buffers[i].id); + command_lists[i] = cmd_buf_info->cmd_list.Get(); + } - command_queue->d3d_queue->ExecuteCommandLists(command_lists.size(), command_lists.ptr()); + command_queue->d3d_queue->ExecuteCommandLists(command_lists.size(), command_lists.ptr()); - for (uint32_t i = 0; i < p_signal_semaphores.size(); i++) { - SemaphoreInfo *semaphore = (SemaphoreInfo *)(p_signal_semaphores[i].id); - semaphore->fence_value++; - command_queue->d3d_queue->Signal(semaphore->d3d_fence.Get(), semaphore->fence_value); - } + for (uint32_t i = 0; i < p_cmd_semaphores.size(); i++) { + SemaphoreInfo *semaphore = (SemaphoreInfo *)(p_cmd_semaphores[i].id); + semaphore->fence_value++; + command_queue->d3d_queue->Signal(semaphore->d3d_fence.Get(), semaphore->fence_value); + } - if (p_signal_fence) { - FenceInfo *fence = (FenceInfo *)(p_signal_fence.id); - fence->fence_value++; - command_queue->d3d_queue->Signal(fence->d3d_fence.Get(), fence->fence_value); - fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle); + if (p_cmd_fence) { + FenceInfo *fence = (FenceInfo *)(p_cmd_fence.id); + fence->fence_value++; + command_queue->d3d_queue->Signal(fence->d3d_fence.Get(), fence->fence_value); + fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle); + } } - return OK; -} - -Error RenderingDeviceDriverD3D12::command_queue_present(CommandQueueID p_cmd_queue, VectorView<SwapChainID> p_swap_chains, VectorView<SemaphoreID> p_wait_semaphores) { - // D3D12 does not require waiting for the command queue's semaphores to handle presentation. - // We just present the swap chains that were specified and ignore the command queue and the semaphores. HRESULT res; bool any_present_failed = false; for (uint32_t i = 0; i < p_swap_chains.size(); i++) { SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); res = swap_chain->d3d_swap_chain->Present(swap_chain->sync_interval, swap_chain->present_flags); if (!SUCCEEDED(res)) { - print_verbose("D3D12: Presenting swapchain failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + print_verbose(vformat("D3D12: Presenting swapchain failed with error 0x%08ux.", (uint64_t)res)); any_present_failed = true; } } diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index 595ee30966..06d5cb65c4 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -413,8 +413,7 @@ private: public: virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override; - virtual Error command_queue_execute(CommandQueueID p_cmd_queue, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_wait_semaphores, VectorView<SemaphoreID> p_signal_semaphores, FenceID p_signal_fence) override; - virtual Error command_queue_present(CommandQueueID p_cmd_queue, VectorView<SwapChainID> p_swap_chains, VectorView<SemaphoreID> p_wait_semaphores) override; + virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override; virtual void command_queue_free(CommandQueueID p_cmd_queue) override; private: diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index f48e6eb7ed..21cf54b4be 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -1113,12 +1113,12 @@ void RenderingDeviceDriverVulkan::_set_object_name(VkObjectType p_object_type, u } Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t p_frame_count) { - // Frame count is not required for the Vulkan driver, so we just ignore it. - context_device = context_driver->device_get(p_device_index); physical_device = context_driver->physical_device_get(p_device_index); vkGetPhysicalDeviceProperties(physical_device, &physical_device_properties); + frame_count = p_frame_count; + // Copy the queue family properties the context already retrieved. uint32_t queue_family_count = context_driver->queue_family_get_count(p_device_index); queue_family_properties.resize(queue_family_count); @@ -2131,21 +2131,18 @@ RDD::CommandQueueID RenderingDeviceDriverVulkan::command_queue_create(CommandQue return CommandQueueID(command_queue); } -Error RenderingDeviceDriverVulkan::command_queue_execute(CommandQueueID p_cmd_queue, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_wait_semaphores, VectorView<SemaphoreID> p_signal_semaphores, FenceID p_signal_fence) { +Error RenderingDeviceDriverVulkan::command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) { DEV_ASSERT(p_cmd_queue.id != 0); + VkResult err; CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); Queue &device_queue = queue_families[command_queue->queue_family][command_queue->queue_index]; - Fence *fence = (Fence *)(p_signal_fence.id); + Fence *fence = (Fence *)(p_cmd_fence.id); VkFence vk_fence = (fence != nullptr) ? fence->vk_fence : VK_NULL_HANDLE; - thread_local LocalVector<VkCommandBuffer> command_buffers; thread_local LocalVector<VkSemaphore> wait_semaphores; - thread_local LocalVector<VkSemaphore> signal_semaphores; thread_local LocalVector<VkPipelineStageFlags> wait_semaphores_stages; - command_buffers.clear(); wait_semaphores.clear(); - signal_semaphores.clear(); wait_semaphores_stages.clear(); if (!command_queue->pending_semaphores_for_execute.is_empty()) { @@ -2158,117 +2155,142 @@ Error RenderingDeviceDriverVulkan::command_queue_execute(CommandQueueID p_cmd_qu command_queue->pending_semaphores_for_execute.clear(); } - for (uint32_t i = 0; i < p_cmd_buffers.size(); i++) { - command_buffers.push_back(VkCommandBuffer(p_cmd_buffers[i].id)); - } - for (uint32_t i = 0; i < p_wait_semaphores.size(); i++) { // FIXME: Allow specifying the stage mask in more detail. wait_semaphores.push_back(VkSemaphore(p_wait_semaphores[i].id)); wait_semaphores_stages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); } - for (uint32_t i = 0; i < p_signal_semaphores.size(); i++) { - signal_semaphores.push_back(VkSemaphore(p_signal_semaphores[i].id)); - } - - VkSubmitInfo submit_info = {}; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.waitSemaphoreCount = wait_semaphores.size(); - submit_info.pWaitSemaphores = wait_semaphores.ptr(); - submit_info.pWaitDstStageMask = wait_semaphores_stages.ptr(); - submit_info.commandBufferCount = command_buffers.size(); - submit_info.pCommandBuffers = command_buffers.ptr(); - submit_info.signalSemaphoreCount = signal_semaphores.size(); - submit_info.pSignalSemaphores = signal_semaphores.ptr(); + if (p_cmd_buffers.size() > 0) { + thread_local LocalVector<VkCommandBuffer> command_buffers; + thread_local LocalVector<VkSemaphore> signal_semaphores; + command_buffers.clear(); + signal_semaphores.clear(); - device_queue.submit_mutex.lock(); - VkResult err = vkQueueSubmit(device_queue.queue, 1, &submit_info, vk_fence); - device_queue.submit_mutex.unlock(); - ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); - - if (fence != nullptr && !command_queue->pending_semaphores_for_fence.is_empty()) { - fence->queue_signaled_from = command_queue; + for (uint32_t i = 0; i < p_cmd_buffers.size(); i++) { + command_buffers.push_back(VkCommandBuffer(p_cmd_buffers[i].id)); + } - // Indicate to the fence that it should release the semaphores that were waited on this submission the next time the fence is waited on. - for (uint32_t i = 0; i < command_queue->pending_semaphores_for_fence.size(); i++) { - command_queue->image_semaphores_for_fences.push_back({ fence, command_queue->pending_semaphores_for_fence[i] }); + for (uint32_t i = 0; i < p_cmd_semaphores.size(); i++) { + signal_semaphores.push_back(VkSemaphore(p_cmd_semaphores[i].id)); } - command_queue->pending_semaphores_for_fence.clear(); - } + VkSemaphore present_semaphore = VK_NULL_HANDLE; + if (p_swap_chains.size() > 0) { + if (command_queue->present_semaphores.is_empty()) { + // Create the semaphores used for presentation if they haven't been created yet. + VkSemaphore semaphore = VK_NULL_HANDLE; + VkSemaphoreCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + for (uint32_t i = 0; i < frame_count; i++) { + err = vkCreateSemaphore(vk_device, &create_info, nullptr, &semaphore); + ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); + command_queue->present_semaphores.push_back(semaphore); + } + } - return OK; -} + // If a presentation semaphore is required, cycle across the ones available on the queue. It is technically possible + // and valid to reuse the same semaphore for this particular operation, but we create multiple ones anyway in case + // some hardware expects multiple semaphores to be used. + present_semaphore = command_queue->present_semaphores[command_queue->present_semaphore_index]; + signal_semaphores.push_back(present_semaphore); + command_queue->present_semaphore_index = (command_queue->present_semaphore_index + 1) % command_queue->present_semaphores.size(); + } -Error RenderingDeviceDriverVulkan::command_queue_present(CommandQueueID p_cmd_queue, VectorView<SwapChainID> p_swap_chains, VectorView<SemaphoreID> p_wait_semaphores) { - DEV_ASSERT(p_cmd_queue.id != 0); + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = wait_semaphores.size(); + submit_info.pWaitSemaphores = wait_semaphores.ptr(); + submit_info.pWaitDstStageMask = wait_semaphores_stages.ptr(); + submit_info.commandBufferCount = command_buffers.size(); + submit_info.pCommandBuffers = command_buffers.ptr(); + submit_info.signalSemaphoreCount = signal_semaphores.size(); + submit_info.pSignalSemaphores = signal_semaphores.ptr(); + + device_queue.submit_mutex.lock(); + err = vkQueueSubmit(device_queue.queue, 1, &submit_info, vk_fence); + device_queue.submit_mutex.unlock(); + ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); + + if (fence != nullptr && !command_queue->pending_semaphores_for_fence.is_empty()) { + fence->queue_signaled_from = command_queue; + + // Indicate to the fence that it should release the semaphores that were waited on this submission the next time the fence is waited on. + for (uint32_t i = 0; i < command_queue->pending_semaphores_for_fence.size(); i++) { + command_queue->image_semaphores_for_fences.push_back({ fence, command_queue->pending_semaphores_for_fence[i] }); + } - CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); - Queue &device_queue = queue_families[command_queue->queue_family][command_queue->queue_index]; + command_queue->pending_semaphores_for_fence.clear(); + } - thread_local LocalVector<VkSwapchainKHR> swapchains; - thread_local LocalVector<uint32_t> image_indices; - thread_local LocalVector<VkSemaphore> wait_semaphores; - thread_local LocalVector<VkResult> results; - swapchains.clear(); - image_indices.clear(); - for (uint32_t i = 0; i < p_swap_chains.size(); i++) { - SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); - swapchains.push_back(swap_chain->vk_swapchain); - DEV_ASSERT(swap_chain->image_index < swap_chain->images.size()); - image_indices.push_back(swap_chain->image_index); + if (present_semaphore != VK_NULL_HANDLE) { + // If command buffers were executed, swap chains must wait on the present semaphore used by the command queue. + wait_semaphores.clear(); + wait_semaphores.push_back(present_semaphore); + } } - wait_semaphores.clear(); - for (uint32_t i = 0; i < p_wait_semaphores.size(); i++) { - wait_semaphores.push_back(VkSemaphore(p_wait_semaphores[i].id)); - } + if (p_swap_chains.size() > 0) { + thread_local LocalVector<VkSwapchainKHR> swapchains; + thread_local LocalVector<uint32_t> image_indices; + thread_local LocalVector<VkResult> results; + swapchains.clear(); + image_indices.clear(); - results.resize(swapchains.size()); - - VkPresentInfoKHR present_info = {}; - present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present_info.waitSemaphoreCount = wait_semaphores.size(); - present_info.pWaitSemaphores = wait_semaphores.ptr(); - present_info.swapchainCount = swapchains.size(); - present_info.pSwapchains = swapchains.ptr(); - present_info.pImageIndices = image_indices.ptr(); - present_info.pResults = results.ptr(); - device_queue.submit_mutex.lock(); - VkResult err = device_functions.QueuePresentKHR(device_queue.queue, &present_info); - device_queue.submit_mutex.unlock(); - - // Set the index to an invalid value. If any of the swap chains returned out of date, indicate it should be resized the next time it's acquired. - bool any_result_is_out_of_date = false; - for (uint32_t i = 0; i < p_swap_chains.size(); i++) { - SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); - swap_chain->image_index = UINT_MAX; - if (results[i] == VK_ERROR_OUT_OF_DATE_KHR) { - context_driver->surface_set_needs_resize(swap_chain->surface, true); - any_result_is_out_of_date = true; + for (uint32_t i = 0; i < p_swap_chains.size(); i++) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); + swapchains.push_back(swap_chain->vk_swapchain); + DEV_ASSERT(swap_chain->image_index < swap_chain->images.size()); + image_indices.push_back(swap_chain->image_index); } - } - if (any_result_is_out_of_date || err == VK_ERROR_OUT_OF_DATE_KHR) { - // It is possible for presentation to fail with out of date while acquire might've succeeded previously. This case - // will be considered a silent failure as it can be triggered easily by resizing a window in the OS natively. - return FAILED; - } + results.resize(swapchains.size()); + + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = wait_semaphores.size(); + present_info.pWaitSemaphores = wait_semaphores.ptr(); + present_info.swapchainCount = swapchains.size(); + present_info.pSwapchains = swapchains.ptr(); + present_info.pImageIndices = image_indices.ptr(); + present_info.pResults = results.ptr(); + + device_queue.submit_mutex.lock(); + err = device_functions.QueuePresentKHR(device_queue.queue, &present_info); + device_queue.submit_mutex.unlock(); + + // Set the index to an invalid value. If any of the swap chains returned out of date, indicate it should be resized the next time it's acquired. + bool any_result_is_out_of_date = false; + for (uint32_t i = 0; i < p_swap_chains.size(); i++) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); + swap_chain->image_index = UINT_MAX; + if (results[i] == VK_ERROR_OUT_OF_DATE_KHR) { + context_driver->surface_set_needs_resize(swap_chain->surface, true); + any_result_is_out_of_date = true; + } + } - // Handling VK_SUBOPTIMAL_KHR the same as VK_SUCCESS is completely intentional. - // - // Godot does not currently support native rotation in Android when creating the swap chain. It intentionally uses - // VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR instead of the current transform bits available in the surface capabilities. - // Choosing the transform that leads to optimal presentation leads to distortion that makes the application unusable, - // as the rotation of all the content is not handled at the moment. - // - // VK_SUBOPTIMAL_KHR is accepted as a successful case even if it's not the most efficient solution to work around this - // problem. This behavior should not be changed unless the swap chain recreation uses the current transform bits, as - // it'll lead to very low performance in Android by entering an endless loop where it'll always resize the swap chain - // every frame. + if (any_result_is_out_of_date || err == VK_ERROR_OUT_OF_DATE_KHR) { + // It is possible for presentation to fail with out of date while acquire might've succeeded previously. This case + // will be considered a silent failure as it can be triggered easily by resizing a window in the OS natively. + return FAILED; + } - ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR, FAILED); + // Handling VK_SUBOPTIMAL_KHR the same as VK_SUCCESS is completely intentional. + // + // Godot does not currently support native rotation in Android when creating the swap chain. It intentionally uses + // VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR instead of the current transform bits available in the surface capabilities. + // Choosing the transform that leads to optimal presentation leads to distortion that makes the application unusable, + // as the rotation of all the content is not handled at the moment. + // + // VK_SUBOPTIMAL_KHR is accepted as a successful case even if it's not the most efficient solution to work around this + // problem. This behavior should not be changed unless the swap chain recreation uses the current transform bits, as + // it'll lead to very low performance in Android by entering an endless loop where it'll always resize the swap chain + // every frame. + + ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR, FAILED); + } return OK; } @@ -2278,6 +2300,11 @@ void RenderingDeviceDriverVulkan::command_queue_free(CommandQueueID p_cmd_queue) CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); + // Erase all the semaphores used for presentation. + for (VkSemaphore semaphore : command_queue->present_semaphores) { + vkDestroySemaphore(vk_device, semaphore, nullptr); + } + // Erase all the semaphores used for image acquisition. for (VkSemaphore semaphore : command_queue->image_semaphores) { vkDestroySemaphore(vk_device, semaphore, nullptr); diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 4abaeecd11..70c4cebba5 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -115,6 +115,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { VkDevice vk_device = VK_NULL_HANDLE; RenderingContextDriverVulkan *context_driver = nullptr; RenderingContextDriver::Device context_device = {}; + uint32_t frame_count = 1; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkPhysicalDeviceProperties physical_device_properties = {}; VkPhysicalDeviceFeatures physical_device_features = {}; @@ -276,6 +277,7 @@ public: // ----- QUEUE ----- private: struct CommandQueue { + LocalVector<VkSemaphore> present_semaphores; LocalVector<VkSemaphore> image_semaphores; LocalVector<SwapChain *> image_semaphores_swap_chains; LocalVector<uint32_t> pending_semaphores_for_execute; @@ -284,12 +286,12 @@ private: LocalVector<Pair<Fence *, uint32_t>> image_semaphores_for_fences; uint32_t queue_family = 0; uint32_t queue_index = 0; + uint32_t present_semaphore_index = 0; }; public: virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override final; - virtual Error command_queue_execute(CommandQueueID p_cmd_queue, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_wait_semaphores, VectorView<SemaphoreID> p_signal_semaphores, FenceID p_signal_fence) override final; - virtual Error command_queue_present(CommandQueueID p_cmd_queue, VectorView<SwapChainID> p_swap_chains, VectorView<SemaphoreID> p_wait_semaphores) override final; + virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override final; virtual void command_queue_free(CommandQueueID p_cmd_queue) override final; private: |