summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_vulkan/vk_resource_manager.h
blob: 08ee86fa6436cd434014fcba66582c3d62b889a8 (plain) (blame)
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
172
173
174
175
176
177
178
179
180
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <cstddef>
#include <memory>
#include <vector>
#include "video_core/renderer_vulkan/declarations.h"

namespace Vulkan {

class VKDevice;
class VKFence;
class VKResourceManager;

class CommandBufferPool;

/// Interface for a Vulkan resource
class VKResource {
public:
    explicit VKResource();
    virtual ~VKResource();

    /**
     * Signals the object that an owning fence has been signaled.
     * @param signaling_fence Fence that signals its usage end.
     */
    virtual void OnFenceRemoval(VKFence* signaling_fence) = 0;
};

/**
 * Fences take ownership of objects, protecting them from GPU-side or driver-side concurrent access.
 * They must be commited from the resource manager. Their usage flow is: commit the fence from the
 * resource manager, protect resources with it and use them, send the fence to an execution queue
 * and Wait for it if needed and then call Release. Used resources will automatically be signaled
 * when they are free to be reused.
 * @brief Protects resources for concurrent usage and signals its release.
 */
class VKFence {
    friend class VKResourceManager;

public:
    explicit VKFence(const VKDevice& device, UniqueFence handle);
    ~VKFence();

    /**
     * Waits for the fence to be signaled.
     * @warning You must have ownership of the fence and it has to be previously sent to a queue to
     * call this function.
     */
    void Wait();

    /**
     * Releases ownership of the fence. Pass after it has been sent to an execution queue.
     * Unmanaged usage of the fence after the call will result in undefined behavior because it may
     * be being used for something else.
     */
    void Release();

    /// Protects a resource with this fence.
    void Protect(VKResource* resource);

    /// Removes protection for a resource.
    void Unprotect(VKResource* resource);

    /// Retreives the fence.
    operator vk::Fence() const {
        return *handle;
    }

private:
    /// Take ownership of the fence.
    void Commit();

    /**
     * Updates the fence status.
     * @warning Waiting for the owner might soft lock the execution.
     * @param gpu_wait Wait for the fence to be signaled by the driver.
     * @param owner_wait Wait for the owner to signal its freedom.
     * @returns True if the fence is free. Waiting for gpu and owner will always return true.
     */
    bool Tick(bool gpu_wait, bool owner_wait);

    const VKDevice& device;                       ///< Device handler
    UniqueFence handle;                           ///< Vulkan fence
    std::vector<VKResource*> protected_resources; ///< List of resources protected by this fence
    bool is_owned = false; ///< The fence has been commited but not released yet.
    bool is_used = false;  ///< The fence has been commited but it has not been checked to be free.
};

/**
 * A fence watch is used to keep track of the usage of a fence and protect a resource or set of
 * resources without having to inherit VKResource from their handlers.
 */
class VKFenceWatch final : public VKResource {
public:
    explicit VKFenceWatch();
    ~VKFenceWatch() override;

    /// Waits for the fence to be released.
    void Wait();

    /**
     * Waits for a previous fence and watches a new one.
     * @param new_fence New fence to wait to.
     */
    void Watch(VKFence& new_fence);

    /**
     * Checks if it's currently being watched and starts watching it if it's available.
     * @returns True if a watch has started, false if it's being watched.
     */
    bool TryWatch(VKFence& new_fence);

    void OnFenceRemoval(VKFence* signaling_fence) override;

private:
    VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
};

/**
 * Handles a pool of resources protected by fences. Manages resource overflow allocating more
 * resources.
 */
class VKFencedPool {
public:
    explicit VKFencedPool(std::size_t grow_step);
    virtual ~VKFencedPool();

protected:
    /**
     * Commits a free resource and protects it with a fence. It may allocate new resources.
     * @param fence Fence that protects the commited resource.
     * @returns Index of the resource commited.
     */
    std::size_t CommitResource(VKFence& fence);

    /// Called when a chunk of resources have to be allocated.
    virtual void Allocate(std::size_t begin, std::size_t end) = 0;

private:
    /// Manages pool overflow allocating new resources.
    std::size_t ManageOverflow();

    /// Allocates a new page of resources.
    void Grow();

    std::size_t grow_step = 0;     ///< Number of new resources created after an overflow
    std::size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
    std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Set of watched resources
};

/**
 * The resource manager handles all resources that can be protected with a fence avoiding
 * driver-side or GPU-side concurrent usage. Usage is documented in VKFence.
 */
class VKResourceManager final {
public:
    explicit VKResourceManager(const VKDevice& device);
    ~VKResourceManager();

    /// Commits a fence. It has to be sent to a queue and released.
    VKFence& CommitFence();

    /// Commits an unused command buffer and protects it with a fence.
    vk::CommandBuffer CommitCommandBuffer(VKFence& fence);

private:
    /// Allocates new fences.
    void GrowFences(std::size_t new_fences_count);

    const VKDevice& device;          ///< Device handler.
    std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
    std::vector<std::unique_ptr<VKFence>> fences;           ///< Pool of fences.
    std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
};

} // namespace Vulkan