2021-12-06 17:13:43 +01:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "buffer.h"
|
|
|
|
|
|
|
|
namespace skyline::gpu {
|
2022-06-05 09:32:33 +02:00
|
|
|
class MegaBuffer;
|
|
|
|
|
2022-04-23 19:10:39 +02:00
|
|
|
/**
|
2022-06-05 09:32:33 +02:00
|
|
|
* @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
|
2022-04-23 19:10:39 +02:00
|
|
|
*/
|
2022-06-05 09:32:33 +02:00
|
|
|
class BufferManager {
|
2022-04-23 19:10:39 +02:00
|
|
|
private:
|
2022-06-05 09:32:33 +02:00
|
|
|
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
|
2022-04-23 19:10:39 +02:00
|
|
|
|
2022-06-26 11:28:58 +02:00
|
|
|
std::mutex megaBufferMutex; //!< Synchronizes access to the allocated megabuffers
|
|
|
|
|
2022-06-05 09:32:33 +02:00
|
|
|
friend class MegaBuffer;
|
2022-04-23 19:10:39 +02:00
|
|
|
|
2022-06-05 09:32:33 +02:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
|
|
|
|
memory::Buffer backing; //!< The GPU buffer as the backing storage for the megabuffer
|
|
|
|
|
|
|
|
MegaBufferSlot(GPU &gpu);
|
|
|
|
};
|
2022-04-23 19:10:39 +02:00
|
|
|
|
2022-07-19 19:12:43 +02:00
|
|
|
/**
|
|
|
|
* @brief A wrapper around a Buffer which locks it with the specified ContextTag
|
|
|
|
*/
|
|
|
|
struct LockedBuffer {
|
|
|
|
std::shared_ptr<Buffer> buffer;
|
|
|
|
ContextLock<Buffer> lock;
|
2022-08-04 12:17:17 +02:00
|
|
|
std::unique_lock<std::recursive_mutex> stateLock;
|
2022-07-19 19:12:43 +02:00
|
|
|
|
|
|
|
LockedBuffer(std::shared_ptr<Buffer> pBuffer, ContextTag tag);
|
|
|
|
|
|
|
|
Buffer *operator->() const;
|
|
|
|
|
|
|
|
std::shared_ptr<Buffer> &operator*();
|
|
|
|
};
|
|
|
|
|
|
|
|
using LockedBuffers = boost::container::small_vector<LockedBuffer, 4>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return A vector of buffers locked with the supplied tag which are contained within the supplied range
|
|
|
|
*/
|
|
|
|
LockedBuffers Lookup(span<u8> range, ContextTag tag);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Inserts the supplied buffer into the map based on its guest address
|
|
|
|
*/
|
|
|
|
void InsertBuffer(std::shared_ptr<Buffer> buffer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Deletes the supplied buffer from the map, the lifetime of the buffer will no longer be extended by the map
|
|
|
|
*/
|
|
|
|
void DeleteBuffer(const std::shared_ptr<Buffer> &buffer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Coalesce the supplied buffers into a single buffer encompassing the specified range and locks it with the supplied tag
|
|
|
|
* @param range The range of memory that the newly created buffer will cover, this will be extended to cover the entirety of the supplied buffers automatically and can be null
|
|
|
|
* @note The supplied buffers **must** be in the map and locked with the supplied tag
|
|
|
|
*/
|
|
|
|
LockedBuffer CoalesceBuffers(span<u8> range, const LockedBuffers &srcBuffers, ContextTag tag);
|
|
|
|
|
2022-04-23 19:10:39 +02:00
|
|
|
/**
|
2022-06-05 09:32:33 +02:00
|
|
|
* @return If the end of the supplied buffer is less than the supplied pointer
|
2022-04-23 19:10:39 +02:00
|
|
|
*/
|
2022-06-05 09:32:33 +02:00
|
|
|
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);
|
2022-04-23 19:10:39 +02:00
|
|
|
|
2022-06-26 11:28:58 +02:00
|
|
|
/**
|
|
|
|
* @brief Acquires an exclusive lock on the texture for the calling thread
|
|
|
|
* @note Naming is in accordance to the BasicLockable named requirement
|
|
|
|
*/
|
|
|
|
void lock();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Relinquishes an existing lock on the texture by the calling thread
|
|
|
|
* @note Naming is in accordance to the BasicLockable named requirement
|
|
|
|
*/
|
|
|
|
void unlock();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread
|
|
|
|
* @note Naming is in accordance to the Lockable named requirement
|
|
|
|
*/
|
|
|
|
bool try_lock();
|
|
|
|
|
2022-04-23 19:10:39 +02:00
|
|
|
/**
|
2022-07-16 16:42:09 +02:00
|
|
|
* @param attachBuffer A function that attaches the buffer to the current context, this'll be called when coalesced buffers are merged into the current buffer
|
2022-06-26 11:02:34 +02:00
|
|
|
* @return A pre-existing or newly created Buffer object which covers the supplied mappings
|
2022-06-26 11:28:58 +02:00
|
|
|
* @note The buffer manager **must** be locked prior to calling this
|
2022-04-23 19:10:39 +02:00
|
|
|
*/
|
2022-07-16 19:00:57 +02:00
|
|
|
BufferView FindOrCreate(GuestBuffer guestMapping, ContextTag tag = {}, const std::function<void(std::shared_ptr<Buffer>, ContextLock<Buffer> &&)> &attachBuffer = {});
|
2022-04-23 19:10:39 +02:00
|
|
|
|
|
|
|
/**
|
2022-06-26 11:02:34 +02:00
|
|
|
* @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
|
2022-06-26 11:28:58 +02:00
|
|
|
* @note The buffer manager **doesn't** need to be locked prior to calling this
|
2022-04-23 19:10:39 +02:00
|
|
|
*/
|
2022-06-26 11:02:34 +02:00
|
|
|
MegaBuffer AcquireMegaBuffer(const std::shared_ptr<FenceCycle> &cycle);
|
2022-04-23 19:10:39 +02:00
|
|
|
};
|
|
|
|
|
2021-12-06 17:13:43 +01:00
|
|
|
/**
|
2022-06-05 09:32:33 +02:00
|
|
|
* @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
|
2021-12-06 17:13:43 +01:00
|
|
|
*/
|
2022-06-05 09:32:33 +02:00
|
|
|
class MegaBuffer {
|
2021-12-06 17:13:43 +01:00
|
|
|
private:
|
2022-06-05 12:20:49 +02:00
|
|
|
BufferManager::MegaBufferSlot *slot;
|
2022-06-05 09:32:33 +02:00
|
|
|
span<u8> freeRegion; //!< The unallocated space in the megabuffer
|
|
|
|
|
|
|
|
public:
|
|
|
|
MegaBuffer(BufferManager::MegaBufferSlot &slot);
|
|
|
|
|
|
|
|
~MegaBuffer();
|
2022-03-28 08:57:05 +02:00
|
|
|
|
2022-06-05 12:20:49 +02:00
|
|
|
MegaBuffer &operator=(MegaBuffer &&other);
|
|
|
|
|
2022-07-07 13:51:09 +02:00
|
|
|
/**
|
|
|
|
* @return If any allocations into the megabuffer were done at the time of the call
|
|
|
|
*/
|
|
|
|
bool WasUsed();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Replaces the cycle associated with the underlying megabuffer with the supplied cycle
|
|
|
|
* @note The megabuffer must **NOT** have any dependencies that aren't conveyed by the supplied cycle
|
|
|
|
*/
|
|
|
|
void ReplaceCycle(const std::shared_ptr<FenceCycle> &cycle);
|
|
|
|
|
2022-03-28 08:57:05 +02:00
|
|
|
/**
|
2022-06-05 09:32:33 +02:00
|
|
|
* @brief Resets the free region of the megabuffer to its initial state, data is left intact but may be overwritten
|
2022-03-28 08:57:05 +02:00
|
|
|
*/
|
2022-06-05 09:32:33 +02:00
|
|
|
void Reset();
|
2022-04-23 19:10:39 +02:00
|
|
|
|
2022-06-05 09:32:33 +02:00
|
|
|
/**
|
|
|
|
* @brief Returns the underlying Vulkan buffer for the megabuffer
|
|
|
|
*/
|
|
|
|
vk::Buffer GetBacking() const;
|
2021-12-06 17:13:43 +01:00
|
|
|
|
|
|
|
/**
|
2022-06-05 09:32:33 +02:00
|
|
|
* @brief Pushes data to the megabuffer and returns the offset at which it was written
|
|
|
|
* @param pageAlign Whether the pushed data should be page aligned in the megabuffer
|
2021-12-06 17:13:43 +01:00
|
|
|
*/
|
2022-06-05 09:32:33 +02:00
|
|
|
vk::DeviceSize Push(span<u8> data, bool pageAlign = false);
|
2021-12-06 17:13:43 +01:00
|
|
|
};
|
|
|
|
}
|