per-thread Vulkan command pool and command buffer management NOTE: since Vulkan is *not* thread-safe, we need to manage this on our own
| 195 | //! per-thread Vulkan command pool and command buffer management |
| 196 | //! NOTE: since Vulkan is *not* thread-safe, we need to manage this on our own |
| 197 | struct vulkan_command_pool_t { |
| 198 | VkCommandPool cmd_pool { nullptr }; |
| 199 | const vulkan_device& dev; |
| 200 | const vulkan_queue& queue; |
| 201 | const bool is_secondary { false }; |
| 202 | const bool no_blocking { false }; |
| 203 | const bool fence_wait_polling { false }; |
| 204 | |
| 205 | vulkan_command_pool_t(const vulkan_device& dev_, const vulkan_queue& queue_, const bool is_secondary_) : |
| 206 | dev(dev_), queue(queue_), is_secondary(is_secondary_), |
| 207 | no_blocking(has_flag<COMPUTE_CONTEXT_FLAGS::VULKAN_NO_BLOCKING>(dev.context->get_context_flags())), |
| 208 | fence_wait_polling(floor::get_vulkan_fence_wait_polling()) {} |
| 209 | |
| 210 | ~vulkan_command_pool_t() { |
| 211 | // NOTE: this is called via vulkan_command_pool_destructor_t on thread exit |
| 212 | { |
| 213 | GUARD(fence_lock); |
| 214 | if (auto fences_in_flight = fences_in_use.count(); fences_in_flight > 0) { |
| 215 | log_warn("#$ fences still in use", fences_in_flight); |
| 216 | } else { |
| 217 | for (const auto& fence : fences) { |
| 218 | vkDestroyFence(dev.device, fence, nullptr); |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | if (cmd_pool) { |
| 223 | vkDestroyCommandPool(dev.device, cmd_pool, nullptr); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | struct command_buffer_internal_t { |
| 228 | vector<shared_ptr<compute_buffer>> retained_buffers; |
| 229 | vector<vulkan_queue::vulkan_completion_handler_t> completion_handlers; |
| 230 | }; |
| 231 | |
| 232 | //! per-thread command buffer and fence count |
| 233 | //! NOTE: since these are *per-thread* we probably never going to need more than this |
| 234 | static constexpr const uint32_t cmd_buffer_count { 64 /* make use of optimized bitset */ }; |
| 235 | static constexpr const uint32_t fence_count { 16 }; |
| 236 | |
| 237 | safe_mutex cmd_buffers_lock; |
| 238 | array<VkCommandBuffer, cmd_buffer_count> cmd_buffers GUARDED_BY(cmd_buffers_lock) {}; |
| 239 | array<command_buffer_internal_t, cmd_buffer_count> cmd_buffer_internals GUARDED_BY(cmd_buffers_lock) {}; |
| 240 | bitset<cmd_buffer_count> cmd_buffers_in_use GUARDED_BY(cmd_buffers_lock) {}; |
| 241 | |
| 242 | safe_mutex fence_lock; |
| 243 | array<VkFence, fence_count> fences GUARDED_BY(fence_lock) {}; |
| 244 | bitset<fence_count> fences_in_use GUARDED_BY(fence_lock) {}; |
| 245 | |
| 246 | //! acquire an unused fence |
| 247 | pair<VkFence, uint32_t> acquire_fence() REQUIRES(!fence_lock) { |
| 248 | for (uint32_t trial = 0, limiter = 10; trial < limiter; ++trial) { |
| 249 | { |
| 250 | GUARD(fence_lock); |
| 251 | if (!fences_in_use.all()) { |
| 252 | for (uint32_t i = 0; i < fence_count; ++i) { |
| 253 | if (!fences_in_use[i]) { |
| 254 | fences_in_use.set(i); |
nothing calls this directly
no test coverage detected