1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
gpu / vulkan / vulkan_fence_helper.h [blame]
// Copyright 2019 The Chromium Authors
// 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_core.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/component_export.h"
#include "base/containers/circular_deque.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "gpu/vulkan/vma_wrapper.h"
namespace gpu {
class VulkanDeviceQueue;
class COMPONENT_EXPORT(VULKAN) VulkanFenceHelper {
public:
explicit VulkanFenceHelper(VulkanDeviceQueue* device_queue);
VulkanFenceHelper(const VulkanFenceHelper&) = delete;
VulkanFenceHelper& operator=(const VulkanFenceHelper&) = delete;
~VulkanFenceHelper();
// Destroy the fence helper.
void Destroy();
// Class representing a fence registered with this system. Should be treated
// as an opaque handle.
class COMPONENT_EXPORT(VULKAN) 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(uint64_t retired_generation_id = 0);
// Helpers for common types:
void EnqueueSemaphoreCleanupForSubmittedWork(VkSemaphore semaphore);
void EnqueueSemaphoresCleanupForSubmittedWork(
std::vector<VkSemaphore> semaphores);
void EnqueueImageCleanupForSubmittedWork(VkImage image,
VkDeviceMemory memory);
void EnqueueBufferCleanupForSubmittedWork(VkBuffer buffer,
VmaAllocation allocation);
// Helpers for VulkanCommandBuffer, VulkanCommandPool, etc
template <typename T>
void EnqueueVulkanObjectCleanupForSubmittedWork(std::unique_ptr<T> obj);
// Careful: this may be very slow, because it synchronizes everything. It is
// intended for cleanup prior to destruction, or when latency is not a concern
// (e.g. when the whole application is backgrounded on Android).
void PerformImmediateCleanup();
private:
const raw_ptr<VulkanDeviceQueue> 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_{this};
};
template <typename T>
void VulkanFenceHelper::EnqueueVulkanObjectCleanupForSubmittedWork(
std::unique_ptr<T> obj) {
if (!obj)
return;
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_