Implement thread-safe MegaBuffer pool

We currently have a global `MegaBuffer` instance that is shared across all channels, this is very problematic as `MegaBuffer` fundamentally works like a state machine with allocations (especially resetting/freeing) and is thread-specific. Therefore, we now have a pool of several `MegaBuffer`s which is allocated from by the `CommandExecutor` and kept channel specific as a result which also limits its usage to a single thread, this allows for individually resetting or freeing any allocations.
This commit is contained in:
PixelyIon
2022-06-05 13:02:33 +05:30
parent 3e08494146
commit a5ca370c36
7 changed files with 126 additions and 79 deletions

View File

@ -6,19 +6,67 @@
#include "buffer.h"
namespace skyline::gpu {
class MegaBuffer;
/**
* @brief The Buffer Manager is responsible for maintaining a global view of buffers being mapped from the guest to the host, any lookups and creation of host buffer from equivalent guest buffer alongside reconciliation of any overlaps with existing textures
*/
class BufferManager {
private:
GPU &gpu;
std::mutex mutex; //!< Synchronizes access to the buffer mappings
std::vector<std::shared_ptr<Buffer>> buffers; //!< A sorted vector of all buffer mappings
friend class MegaBuffer;
/**
* @brief A wrapper around a buffer which can be utilized as backing storage for a megabuffer and can track its state to avoid concurrent usage
*/
struct MegaBufferSlot {
std::atomic_flag active{true}; //!< If the megabuffer is currently being utilized, we want to construct a buffer as active
std::shared_ptr<FenceCycle> cycle; //!< The latest cycle on the fence, all waits must be performed through this
constexpr static vk::DeviceSize Size{100 * 1024 * 1024}; //!< Size in bytes of the megabuffer (100MiB)
memory::Buffer backing; //!< The GPU buffer as the backing storage for the megabuffer
MegaBufferSlot(GPU &gpu);
};
/**
* @return If the end of the supplied buffer is less than the supplied pointer
*/
static bool BufferLessThan(const std::shared_ptr<Buffer> &it, u8 *pointer);
public:
std::list<MegaBufferSlot> megaBuffers; //!< A pool of all allocated megabuffers, these are dynamically utilized
BufferManager(GPU &gpu);
/**
* @return A dynamically allocated megabuffer which can be used to store buffer modifications allowing them to be replayed in-sequence on the GPU
* @note This object **must** be destroyed to be reclaimed by the manager and prevent a memory leak
*/
MegaBuffer AcquireMegaBuffer(const std::shared_ptr<FenceCycle> &cycle);
/**
* @return A pre-existing or newly created Buffer object which covers the supplied mappings
*/
BufferView FindOrCreate(GuestBuffer guestMapping, const std::shared_ptr<FenceCycle> &cycle = nullptr);
};
/**
* @brief A simple linearly allocated GPU-side buffer used to temporarily store buffer modifications allowing them to be replayed in-sequence on the GPU
* @note This class is **not** thread-safe and any calls must be externally synchronized
*/
class MegaBuffer {
private:
constexpr static vk::DeviceSize Size{0x6'400'000}; //!< Size in bytes of the megabuffer (100MiB)
memory::Buffer backing; //!< The backing GPU buffer
std::mutex mutex; //!< Synchronizes access to freeRegion
span<u8> freeRegion; //!< Span of unallocated space in the megabuffer
BufferManager::MegaBufferSlot &slot;
span<u8> freeRegion; //!< The unallocated space in the megabuffer
public:
MegaBuffer(GPU &gpu);
MegaBuffer(BufferManager::MegaBufferSlot &slot);
~MegaBuffer();
/**
* @brief Resets the free region of the megabuffer to its initial state, data is left intact but may be overwritten
@ -36,29 +84,4 @@ namespace skyline::gpu {
*/
vk::DeviceSize Push(span<u8> data, bool pageAlign = false);
};
/**
* @brief The Buffer Manager is responsible for maintaining a global view of buffers being mapped from the guest to the host, any lookups and creation of host buffer from equivalent guest buffer alongside reconciliation of any overlaps with existing textures
*/
class BufferManager {
private:
GPU &gpu;
std::mutex mutex; //!< Synchronizes access to the buffer mappings
std::vector<std::shared_ptr<Buffer>> buffers; //!< A sorted vector of all buffer mappings
/**
* @return If the end of the supplied buffer is less than the supplied pointer
*/
static bool BufferLessThan(const std::shared_ptr<Buffer> &it, u8 *pointer);
public:
MegaBuffer megaBuffer; //!< The megabuffer used to temporarily store buffer modifications allowing them to be replayed in-sequence on the GPU
BufferManager(GPU &gpu);
/**
* @return A pre-existing or newly created Buffer object which covers the supplied mappings
*/
BufferView FindOrCreate(GuestBuffer guestMapping, const std::shared_ptr<FenceCycle> &cycle = nullptr);
};
}