diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/out/vulkan/common.h | 4 | ||||
-rw-r--r-- | video/out/vulkan/utils.c | 111 | ||||
-rw-r--r-- | video/out/vulkan/utils.h | 29 |
3 files changed, 144 insertions, 0 deletions
diff --git a/video/out/vulkan/common.h b/video/out/vulkan/common.h index 35c5b3dbfb..de49c6f1af 100644 --- a/video/out/vulkan/common.h +++ b/video/out/vulkan/common.h @@ -53,6 +53,10 @@ struct mpvk_ctx { struct vk_cmd *last_cmd; // most recently submitted (pending) command struct spirv_compiler *spirv; // GLSL -> SPIR-V compiler + // Common pool of signals, to avoid having to re-create these objects often + struct vk_signal **signals; + int num_signals; + // Cached capabilities VkPhysicalDeviceLimits limits; }; diff --git a/video/out/vulkan/utils.c b/video/out/vulkan/utils.c index ee5a524947..9d9d8d9820 100644 --- a/video/out/vulkan/utils.c +++ b/video/out/vulkan/utils.c @@ -140,6 +140,9 @@ void mpvk_uninit(struct mpvk_ctx *vk) if (vk->dev) { vk_cmdpool_destroy(vk, vk->pool); + for (int i = 0; i < vk->num_signals; i++) + vk_signal_destroy(vk, &vk->signals[i]); + talloc_free(vk->signals); vk_malloc_uninit(vk); vkDestroyDevice(vk->dev, MPVK_ALLOCATOR); } @@ -726,6 +729,114 @@ error: return ret; } +void vk_signal_destroy(struct mpvk_ctx *vk, struct vk_signal **sig) +{ + if (!*sig) + return; + + vkDestroySemaphore(vk->dev, (*sig)->semaphore, MPVK_ALLOCATOR); + vkDestroyEvent(vk->dev, (*sig)->event, MPVK_ALLOCATOR); + talloc_free(*sig); + *sig = NULL; +} + +struct vk_signal *vk_cmd_signal(struct mpvk_ctx *vk, struct vk_cmd *cmd, + VkPipelineStageFlags stage) +{ + struct vk_signal *sig = NULL; + if (MP_TARRAY_POP(vk->signals, vk->num_signals, &sig)) + goto done; + + // no available signal => initialize a new one + sig = talloc_zero(NULL, struct vk_signal); + static const VkSemaphoreCreateInfo sinfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + + VK(vkCreateSemaphore(vk->dev, &sinfo, MPVK_ALLOCATOR, &sig->semaphore)); + + static const VkEventCreateInfo einfo = { + .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, + }; + + VK(vkCreateEvent(vk->dev, &einfo, MPVK_ALLOCATOR, &sig->event)); + +done: + // Signal both the semaphore and the event. (We will only end up using one) + vk_cmd_sig(cmd, sig->semaphore); + vkCmdSetEvent(cmd->buf, sig->event, stage); + sig->event_source = cmd->queue; + return sig; + +error: + vk_signal_destroy(vk, &sig); + return NULL; +} + +static bool unsignal_cmd(struct vk_cmd *cmd, VkSemaphore sem) +{ + for (int n = 0; n < cmd->num_sigs; n++) { + if (cmd->sigs[n] == sem) { + MP_TARRAY_REMOVE_AT(cmd->sigs, cmd->num_sigs, n); + return true; + } + } + + return false; +} + +// Attempts to remove a queued signal operation. Returns true if sucessful, +// i.e. the signal could be removed before it ever got fired. +static bool unsignal(struct vk_cmd *cmd, VkSemaphore sem) +{ + if (unsignal_cmd(cmd, sem)) + return true; + + // Attempt to remove it from any queued commands + for (int i = 0; i < cmd->pool->num_cmds_queued; i++) { + if (unsignal_cmd(cmd->pool->cmds_queued[i], sem)) + return true; + } + + return false; +} + +static void release_signal(struct mpvk_ctx *vk, struct vk_signal *sig) +{ + // The semaphore never needs to be recreated, because it's either + // unsignaled while still queued, or unsignaled as a result of a device + // wait. But the event *may* need to be reset, so just always reset it. + vkResetEvent(vk->dev, sig->event); + MP_TARRAY_APPEND(NULL, vk->signals, vk->num_signals, sig); +} + +void vk_cmd_wait(struct mpvk_ctx *vk, struct vk_cmd *cmd, + struct vk_signal **sigptr, VkPipelineStageFlags stage, + VkEvent *out_event) +{ + struct vk_signal *sig = *sigptr; + if (!sig) + return; + + if (out_event && sig->event && sig->event_source == cmd->queue && + unsignal(cmd, sig->semaphore)) + { + // If we can remove the semaphore signal operation from the history and + // pretend it never happened, then we get to use the VkEvent. This also + // requires that the VkEvent was signalled from the same VkQueue. + *out_event = sig->event; + } else if (sig->semaphore) { + // Otherwise, we use the semaphore. (This also unsignals it as a result + // of the command execution) + vk_cmd_dep(cmd, sig->semaphore, stage); + } + + // In either case, once the command completes, we can release the signal + // resource back to the pool. + vk_cmd_callback(cmd, (vk_cb) release_signal, vk, sig); + *sigptr = NULL; +} + const VkImageSubresourceRange vk_range = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, diff --git a/video/out/vulkan/utils.h b/video/out/vulkan/utils.h index bdbbe0aa70..538897afae 100644 --- a/video/out/vulkan/utils.h +++ b/video/out/vulkan/utils.h @@ -121,6 +121,35 @@ void vk_cmd_dep(struct vk_cmd *cmd, VkSemaphore dep, VkPipelineStageFlags stage) // after the command completes. void vk_cmd_sig(struct vk_cmd *cmd, VkSemaphore sig); +// Signal abstraction: represents an abstract synchronization mechanism. +// Internally, this may either resolve as a semaphore or an event depending +// on whether the appropriate conditions are met. +struct vk_signal { + VkSemaphore semaphore; + VkEvent event; + VkQueue event_source; +}; + +// Generates a signal after the execution of all previous commands matching the +// given the pipeline stage. The signal is owned by the caller, and must be +// consumed eith vk_cmd_wait or released with vk_signal_cancel in order to +// free the resources. +struct vk_signal *vk_cmd_signal(struct mpvk_ctx *vk, struct vk_cmd *cmd, + VkPipelineStageFlags stage); + +// Consumes a previously generated signal. This signal must fire by the +// indicated stage before the command can run. If *event is not NULL, then it +// MAY be set to a VkEvent which the caller MUST manually wait on in the most +// appropriate way. This function takes over ownership of the signal (and the +// signal will be released/reused automatically) +void vk_cmd_wait(struct mpvk_ctx *vk, struct vk_cmd *cmd, + struct vk_signal **sigptr, VkPipelineStageFlags stage, + VkEvent *out_event); + +// Destroys a currently pending signal, for example if the resource is no +// longer relevant. +void vk_signal_destroy(struct mpvk_ctx *vk, struct vk_signal **sig); + // Command pool / queue family hybrid abstraction struct vk_cmdpool { VkQueueFamilyProperties props; |