123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- // Copyright (c) 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "gpu/vulkan/vulkan_fence_helper.h"
- #include "base/bind.h"
- #include "base/logging.h"
- #include "gpu/vulkan/vulkan_device_queue.h"
- #include "gpu/vulkan/vulkan_function_pointers.h"
- namespace gpu {
- VulkanFenceHelper::FenceHandle::FenceHandle() = default;
- VulkanFenceHelper::FenceHandle::FenceHandle(VkFence fence,
- uint64_t generation_id)
- : fence_(fence), generation_id_(generation_id) {}
- VulkanFenceHelper::FenceHandle::FenceHandle(const FenceHandle& other) = default;
- VulkanFenceHelper::FenceHandle& VulkanFenceHelper::FenceHandle::operator=(
- const FenceHandle& other) = default;
- VulkanFenceHelper::VulkanFenceHelper(VulkanDeviceQueue* device_queue)
- : device_queue_(device_queue) {}
- VulkanFenceHelper::~VulkanFenceHelper() {
- DCHECK(tasks_pending_fence_.empty());
- DCHECK(cleanup_tasks_.empty());
- }
- void VulkanFenceHelper::Destroy() {
- PerformImmediateCleanup();
- }
- // TODO(ericrk): Handle recycling fences.
- VkResult VulkanFenceHelper::GetFence(VkFence* fence) {
- VkFenceCreateInfo create_info{
- .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- };
- return vkCreateFence(device_queue_->GetVulkanDevice(), &create_info,
- nullptr /* pAllocator */, fence);
- }
- VulkanFenceHelper::FenceHandle VulkanFenceHelper::EnqueueFence(VkFence fence) {
- FenceHandle handle(fence, next_generation_++);
- cleanup_tasks_.emplace_back(handle, std::move(tasks_pending_fence_));
- tasks_pending_fence_ = std::vector<CleanupTask>();
- return handle;
- }
- bool VulkanFenceHelper::Wait(FenceHandle handle,
- uint64_t timeout_in_nanoseconds) {
- if (HasPassed(handle))
- return true;
- VkResult result =
- vkWaitForFences(device_queue_->GetVulkanDevice(), 1, &handle.fence_, true,
- timeout_in_nanoseconds);
- // After waiting, we can process cleanup tasks.
- ProcessCleanupTasks();
- return result == VK_SUCCESS;
- }
- bool VulkanFenceHelper::HasPassed(FenceHandle handle) {
- // Process cleanup tasks which advances our |current_generation_|.
- ProcessCleanupTasks();
- return current_generation_ >= handle.generation_id_;
- }
- void VulkanFenceHelper::EnqueueCleanupTaskForSubmittedWork(CleanupTask task) {
- tasks_pending_fence_.emplace_back(std::move(task));
- }
- void VulkanFenceHelper::ProcessCleanupTasks(uint64_t retired_generation_id) {
- VkDevice device = device_queue_->GetVulkanDevice();
- if (!retired_generation_id)
- retired_generation_id = current_generation_;
- // Iterate over our pending cleanup fences / tasks, advancing
- // |current_generation_| as far as possible.
- for (const auto& tasks_for_fence : cleanup_tasks_) {
- // Callback based tasks have no actual fence to wait on, keep checking
- // future fences, as a callback may be delayed.
- if (tasks_for_fence.UsingCallback())
- continue;
- VkResult result = vkGetFenceStatus(device, tasks_for_fence.fence);
- if (result == VK_NOT_READY) {
- retired_generation_id =
- std::min(retired_generation_id, tasks_for_fence.generation_id - 1);
- break;
- }
- if (result == VK_SUCCESS) {
- retired_generation_id =
- std::max(tasks_for_fence.generation_id, retired_generation_id);
- continue;
- }
- DLOG(ERROR) << "vkGetFenceStatus() failed: " << result;
- PerformImmediateCleanup();
- return;
- }
- current_generation_ = retired_generation_id;
- // Runs any cleanup tasks for generations that have passed. Create a temporary
- // vector of tasks to run to avoid reentrancy issues.
- std::vector<CleanupTask> tasks_to_run;
- while (!cleanup_tasks_.empty()) {
- TasksForFence& tasks_for_fence = cleanup_tasks_.front();
- if (tasks_for_fence.generation_id > current_generation_)
- break;
- if (tasks_for_fence.fence != VK_NULL_HANDLE) {
- DCHECK_EQ(vkGetFenceStatus(device, tasks_for_fence.fence), VK_SUCCESS);
- vkDestroyFence(device, tasks_for_fence.fence, nullptr);
- }
- tasks_to_run.insert(tasks_to_run.end(),
- std::make_move_iterator(tasks_for_fence.tasks.begin()),
- std::make_move_iterator(tasks_for_fence.tasks.end()));
- cleanup_tasks_.pop_front();
- }
- for (auto& task : tasks_to_run)
- std::move(task).Run(device_queue_.get(), false /* device_lost */);
- }
- VulkanFenceHelper::FenceHandle VulkanFenceHelper::GenerateCleanupFence() {
- if (tasks_pending_fence_.empty())
- return FenceHandle();
- VkFence fence = VK_NULL_HANDLE;
- VkResult result = GetFence(&fence);
- if (result != VK_SUCCESS) {
- PerformImmediateCleanup();
- return FenceHandle();
- }
- result = vkQueueSubmit(device_queue_->GetVulkanQueue(), 0, nullptr, fence);
- if (result != VK_SUCCESS) {
- vkDestroyFence(device_queue_->GetVulkanDevice(), fence, nullptr);
- PerformImmediateCleanup();
- return FenceHandle();
- }
- return EnqueueFence(fence);
- }
- base::OnceClosure VulkanFenceHelper::CreateExternalCallback() {
- // No need to do callback tracking if there are no cleanup tasks to run.
- if (tasks_pending_fence_.empty())
- return base::OnceClosure();
- // Get a generation ID for this callback and associate existing cleanup
- // tasks.
- uint64_t generation_id = next_generation_++;
- cleanup_tasks_.emplace_back(generation_id, std::move(tasks_pending_fence_));
- tasks_pending_fence_ = std::vector<CleanupTask>();
- return base::BindOnce(
- [](base::WeakPtr<VulkanFenceHelper> fence_helper,
- uint64_t generation_id) {
- if (!fence_helper)
- return;
- // If |current_generation_| is ahead of the callback's
- // |generation_id|, the callback came late. Ignore it.
- if (generation_id > fence_helper->current_generation_) {
- fence_helper->ProcessCleanupTasks(generation_id);
- }
- },
- weak_factory_.GetWeakPtr(), generation_id);
- }
- void VulkanFenceHelper::EnqueueSemaphoreCleanupForSubmittedWork(
- VkSemaphore semaphore) {
- if (semaphore == VK_NULL_HANDLE)
- return;
- EnqueueSemaphoresCleanupForSubmittedWork({semaphore});
- }
- void VulkanFenceHelper::EnqueueSemaphoresCleanupForSubmittedWork(
- std::vector<VkSemaphore> semaphores) {
- if (semaphores.empty())
- return;
- EnqueueCleanupTaskForSubmittedWork(base::BindOnce(
- [](std::vector<VkSemaphore> semaphores, VulkanDeviceQueue* device_queue,
- bool /* is_lost */) {
- for (VkSemaphore semaphore : semaphores) {
- vkDestroySemaphore(device_queue->GetVulkanDevice(), semaphore,
- nullptr);
- }
- },
- std::move(semaphores)));
- }
- void VulkanFenceHelper::EnqueueImageCleanupForSubmittedWork(
- VkImage image,
- VkDeviceMemory memory) {
- if (image == VK_NULL_HANDLE && memory == VK_NULL_HANDLE)
- return;
- EnqueueCleanupTaskForSubmittedWork(base::BindOnce(
- [](VkImage image, VkDeviceMemory memory, VulkanDeviceQueue* device_queue,
- bool /* is_lost */) {
- if (image != VK_NULL_HANDLE)
- vkDestroyImage(device_queue->GetVulkanDevice(), image, nullptr);
- if (memory != VK_NULL_HANDLE)
- vkFreeMemory(device_queue->GetVulkanDevice(), memory, nullptr);
- },
- image, memory));
- }
- void VulkanFenceHelper::EnqueueBufferCleanupForSubmittedWork(
- VkBuffer buffer,
- VmaAllocation allocation) {
- if (buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
- return;
- DCHECK(buffer != VK_NULL_HANDLE);
- DCHECK(allocation != VK_NULL_HANDLE);
- EnqueueCleanupTaskForSubmittedWork(base::BindOnce(
- [](VkBuffer buffer, VmaAllocation allocation,
- VulkanDeviceQueue* device_queue, bool /* is_lost */) {
- vma::DestroyBuffer(device_queue->vma_allocator(), buffer, allocation);
- },
- buffer, allocation));
- }
- void VulkanFenceHelper::PerformImmediateCleanup() {
- if (cleanup_tasks_.empty() && tasks_pending_fence_.empty())
- return;
- // We want to run all tasks immediately, so just use vkQueueWaitIdle which
- // ensures that all fences have passed.
- // Even if exclusively using callbacks, the callbacks use WeakPtr and will
- // not keep this class alive, so it's important to wait / run all cleanup
- // immediately.
- VkResult result = vkQueueWaitIdle(device_queue_->GetVulkanQueue());
- // Wait can only fail for three reasons - device loss, host OOM, device OOM.
- // If we hit an OOM, treat this as a crash. There isn't a great way to
- // recover from this.
- CHECK(result == VK_SUCCESS || result == VK_ERROR_DEVICE_LOST);
- bool device_lost = result == VK_ERROR_DEVICE_LOST;
- // We're going to destroy all fences below, so we should consider them as
- // passed.
- current_generation_ = next_generation_ - 1;
- // Run all cleanup tasks. Create a temporary vector of tasks to run to avoid
- // reentrancy issues.
- std::vector<CleanupTask> tasks_to_run;
- while (!cleanup_tasks_.empty()) {
- auto& tasks_for_fence = cleanup_tasks_.front();
- vkDestroyFence(device_queue_->GetVulkanDevice(), tasks_for_fence.fence,
- nullptr);
- tasks_to_run.insert(tasks_to_run.end(),
- std::make_move_iterator(tasks_for_fence.tasks.begin()),
- std::make_move_iterator(tasks_for_fence.tasks.end()));
- cleanup_tasks_.pop_front();
- }
- tasks_to_run.insert(tasks_to_run.end(),
- std::make_move_iterator(tasks_pending_fence_.begin()),
- std::make_move_iterator(tasks_pending_fence_.end()));
- tasks_pending_fence_.clear();
- for (auto& task : tasks_to_run)
- std::move(task).Run(device_queue_.get(), device_lost);
- }
- VulkanFenceHelper::TasksForFence::TasksForFence(FenceHandle handle,
- std::vector<CleanupTask> tasks)
- : fence(handle.fence_),
- generation_id(handle.generation_id_),
- tasks(std::move(tasks)) {}
- VulkanFenceHelper::TasksForFence::TasksForFence(uint64_t generation_id,
- std::vector<CleanupTask> tasks)
- : generation_id(generation_id), tasks(std::move(tasks)) {}
- VulkanFenceHelper::TasksForFence::~TasksForFence() = default;
- VulkanFenceHelper::TasksForFence::TasksForFence(TasksForFence&& other) =
- default;
- } // namespace gpu
|