blob: 7bbef05d1a94f7b27598be342cfcf693f5c43aac [file] [log] [blame]
// 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.
#ifndef GPU_VULKAN_VULKAN_FENCE_HELPER_H_
#define GPU_VULKAN_VULKAN_FENCE_HELPER_H_
#include <vulkan/vulkan.h>
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "gpu/vulkan/vulkan_export.h"
namespace gpu {
class VulkanDeviceQueue;
class VULKAN_EXPORT VulkanFenceHelper {
public:
explicit VulkanFenceHelper(VulkanDeviceQueue* device_queue);
~VulkanFenceHelper();
// Destroy the fence helper.
void Destroy();
// Class representing a fence registered with this system. Should be treated
// as an opaque handle.
class FenceHandle {
public:
FenceHandle();
FenceHandle(const FenceHandle& other);
FenceHandle& operator=(const FenceHandle& other);
bool is_valid() const { return fence_ != VK_NULL_HANDLE; }
private:
friend class VulkanFenceHelper;
FenceHandle(VkFence fence, uint64_t generation_id);
VkFence fence_ = VK_NULL_HANDLE;
uint64_t generation_id_ = 0;
};
// General fence management functions. Should be used by any Chrome code
// which creates / submits fences. By registering fences with this class and
// checking them via the returned FenceHandle, we are able to leverage these
// same fences for running cleanup tasks.
//
// In typical cases, callers will call GetFence to generate/reuse a fence,
// submit this fence, then call EnqueueFence to register it with this system.
//
// In cases where fences are not being generated by Chrome, consumers should
// ensure that GenerateCleanupFence is called once per frame to allow cleanup
// tasks to be processed.
//
// Creates or recycles a fence.
VkResult GetFence(VkFence* fence);
// Enqueues a fence which must eventually signal (must have been submitted).
// This function takes ownership of the fence. Returns a FenceHandle which
// can be used to wait on this fence / check status.
// Note: This should be called immediately after submitting a fence, as
// calling this will attach cleanup tasks to the fence. If cleanup tasks
// are able to be inserted between fence submission and this call, we can
// end up with incorrect cleanup.
FenceHandle EnqueueFence(VkFence fence);
// Generates and submits a fence.
// TODO(ericrk): We should avoid this in all cases if possible.
FenceHandle GenerateCleanupFence();
// Creates a callback that calls pending cleanup tasks. Used in cases where an
// external component (Skia) is submitting / waiting on a fence and cannot
// share that fence with this class.
// Note: It is important that no new cleanup tasks or fences are inserted
// between this call and the submission of the fence which will eventually
// trigger this callback. Doing so could cause the callbacks associated
// with this call to run out of order / incorrectly.
base::OnceClosure CreateExternalCallback();
// Helper functions which allow clients to wait for or check the statusof a
// fence submitted with EnqueueFence.
//
// Waits for the given fence associated with the given generation id to pass.
bool Wait(FenceHandle handle, uint64_t timeout_in_nanoseconds = UINT64_MAX);
// Checks whether the given generation id has passed.
bool HasPassed(FenceHandle handle);
// Cleanup helpers. Allow callers to enqueue cleanup tasks which will be run
// after the next fence provided by EnqueueFence or GenerateCleanupFence
// passes. Tasks must only be enqueued if all relevant work has already been
// submitted to the queue - it must be the case that the task can immediately
// be run after a vkQueueWaitIdle. To ensure that cleanup tasks run, callers
// should ensure that ProcessCleanupTasks is called once per frame.
using CleanupTask = base::OnceCallback<void(VulkanDeviceQueue* device_queue,
bool device_lost)>;
// Submits a cleanup task for already submitted work. ProcessCleanupTasks
// must be called periodically to ensure these run. Cleanup tasks will be
// executed in order they are enqueued.
void EnqueueCleanupTaskForSubmittedWork(CleanupTask task);
// Processes CleanupTasks for which a fence has passed.
void ProcessCleanupTasks();
// Helpers for common types:
void EnqueueSemaphoreCleanupForSubmittedWork(VkSemaphore semaphore);
void EnqueueSemaphoresCleanupForSubmittedWork(
std::vector<VkSemaphore> semaphores);
void EnqueueImageCleanupForSubmittedWork(VkImage image,
VkDeviceMemory memory);
void EnqueueBufferCleanupForSubmittedWork(VkBuffer buffer,
VkDeviceMemory memory);
// Helpers for VulkanCommandBuffer, VulkanCommandPool, etc
template <typename T>
void EnqueueVulkanObjectCleanupForSubmittedWork(std::unique_ptr<T> obj);
private:
void PerformImmediateCleanup();
VulkanDeviceQueue* const device_queue_;
std::vector<CleanupTask> tasks_pending_fence_;
uint64_t next_generation_ = 1;
uint64_t current_generation_ = 0;
struct TasksForFence {
// Constructor when tasks associated with a fence.
TasksForFence(FenceHandle handle, std::vector<CleanupTask> tasks);
// Constructor when tasks associated with Skia callback.
TasksForFence(uint64_t generation_id, std::vector<CleanupTask> tasks);
~TasksForFence();
TasksForFence(TasksForFence&& other);
TasksForFence& operator=(TasksForFence&& other);
bool UsingCallback() const { return fence == VK_NULL_HANDLE; }
const VkFence fence = VK_NULL_HANDLE;
const uint64_t generation_id = 0;
std::vector<CleanupTask> tasks;
};
base::circular_deque<TasksForFence> cleanup_tasks_;
base::WeakPtrFactory<VulkanFenceHelper> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(VulkanFenceHelper);
};
template <typename T>
void VulkanFenceHelper::EnqueueVulkanObjectCleanupForSubmittedWork(
std::unique_ptr<T> obj) {
EnqueueCleanupTaskForSubmittedWork(
base::BindOnce([](std::unique_ptr<T> obj, VulkanDeviceQueue* device_queue,
bool device_lost) { obj->Destroy(); },
std::move(obj)));
}
} // namespace gpu
#endif // GPU_VULKAN_VULKAN_FENCE_HELPER_H_