mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 10:05:29 +03:00
Introduce BufferManager
The Buffer Manager handles mapping of guest buffers to host buffer views with automatic handling of sub-buffers and eventually supporting recreation of overlapping buffers to create a single larger buffer.
This commit is contained in:
parent
bde61d72cc
commit
03314ec7d2
@ -157,6 +157,7 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/gpu/quirk_manager.cpp
|
||||
${source_DIR}/skyline/gpu/memory_manager.cpp
|
||||
${source_DIR}/skyline/gpu/texture_manager.cpp
|
||||
${source_DIR}/skyline/gpu/buffer_manager.cpp
|
||||
${source_DIR}/skyline/gpu/command_scheduler.cpp
|
||||
${source_DIR}/skyline/gpu/texture/texture.cpp
|
||||
${source_DIR}/skyline/gpu/buffer.cpp
|
||||
|
@ -213,5 +213,6 @@ namespace skyline::gpu {
|
||||
scheduler(*this),
|
||||
presentation(state, *this),
|
||||
texture(*this),
|
||||
buffer(*this),
|
||||
shader(state, *this) {}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "gpu/command_scheduler.h"
|
||||
#include "gpu/presentation_engine.h"
|
||||
#include "gpu/texture_manager.h"
|
||||
#include "gpu/buffer_manager.h"
|
||||
#include "gpu/shader_manager.h"
|
||||
|
||||
namespace skyline::gpu {
|
||||
@ -45,6 +46,7 @@ namespace skyline::gpu {
|
||||
PresentationEngine presentation;
|
||||
|
||||
TextureManager texture;
|
||||
BufferManager buffer;
|
||||
|
||||
ShaderManager shader;
|
||||
|
||||
|
@ -21,6 +21,7 @@ namespace skyline::gpu {
|
||||
};
|
||||
|
||||
struct BufferView;
|
||||
class BufferManager;
|
||||
|
||||
/**
|
||||
* @brief A buffer which is backed by host constructs while being synchronized with the underlying guest buffer
|
||||
@ -36,6 +37,7 @@ namespace skyline::gpu {
|
||||
std::vector<std::weak_ptr<BufferView>> views; //!< BufferView(s) that are backed by this Buffer, used for repointing to a new Buffer on deletion
|
||||
|
||||
friend BufferView;
|
||||
friend BufferManager;
|
||||
|
||||
public:
|
||||
std::weak_ptr<FenceCycle> cycle; //!< A fence cycle for when any host operation mutating the buffer has completed, it must be waited on prior to any mutations to the backing
|
||||
|
108
app/src/main/cpp/skyline/gpu/buffer_manager.cpp
Normal file
108
app/src/main/cpp/skyline/gpu/buffer_manager.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <gpu.h>
|
||||
|
||||
#include "buffer_manager.h"
|
||||
|
||||
namespace skyline::gpu {
|
||||
BufferManager::BufferManager(GPU &gpu) : gpu(gpu) {}
|
||||
|
||||
std::shared_ptr<BufferView> BufferManager::FindOrCreate(const GuestBuffer &guest) {
|
||||
auto guestMapping{guest.mappings.front()};
|
||||
|
||||
/*
|
||||
* Iterate over all buffers that overlap with the first mapping of the guest buffer and compare the mappings:
|
||||
* 1) All mappings match up perfectly, we check that the rest of the supplied mappings correspond to mappings in the buffer
|
||||
* 1.1) If they match as well, we return a view encompassing the entire buffer
|
||||
* 2) Only a contiguous range of mappings match, we check for the overlap bounds, it can go two ways:
|
||||
* 2.1) If the supplied buffer is smaller than the matching buffer, we return a view encompassing the mappings into the buffer
|
||||
* 2.2) If the matching buffer is smaller than the supplied buffer, we make the matching buffer larger and return it
|
||||
* 3) If there's another overlap we go back to (1) with it else we go to (4)
|
||||
* 4) Create a new buffer and insert it in the map then return it
|
||||
*/
|
||||
|
||||
std::scoped_lock lock(mutex);
|
||||
std::shared_ptr<Buffer> match{};
|
||||
auto mappingEnd{std::upper_bound(buffers.begin(), buffers.end(), guestMapping)}, hostMapping{mappingEnd};
|
||||
if (hostMapping != buffers.begin() && (--hostMapping)->end() > guestMapping.begin()) {
|
||||
auto &hostMappings{hostMapping->buffer->guest.mappings};
|
||||
if (hostMapping->contains(guestMapping)) {
|
||||
// We need to check that all corresponding mappings in the candidate buffer and the guest buffer match up
|
||||
// Only the start of the first matched mapping and the end of the last mapping can not match up as this is the case for views
|
||||
auto firstHostMapping{hostMapping->iterator};
|
||||
auto lastGuestMapping{guest.mappings.back()};
|
||||
auto endHostMapping{std::find_if(firstHostMapping, hostMappings.end(), [&lastGuestMapping](const span<u8> &it) {
|
||||
return lastGuestMapping.begin() > it.begin() && lastGuestMapping.end() > it.end();
|
||||
})}; //!< A past-the-end iterator for the last host mapping, the final valid mapping is prior to this iterator
|
||||
bool mappingMatch{std::equal(firstHostMapping, endHostMapping, guest.mappings.begin(), guest.mappings.end(), [](const span<u8> &lhs, const span<u8> &rhs) {
|
||||
return lhs.end() == rhs.end(); // We check end() here to implicitly ignore any offset from the first mapping
|
||||
})};
|
||||
|
||||
auto &lastHostMapping{*std::prev(endHostMapping)};
|
||||
if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && endHostMapping == hostMappings.end() && lastGuestMapping.end() == lastHostMapping.end()) {
|
||||
// We've gotten a perfect 1:1 match for *all* mappings from the start to end
|
||||
std::scoped_lock bufferLock(*hostMapping->buffer);
|
||||
return hostMapping->buffer->GetView(0, hostMapping->buffer->size, guest.format);
|
||||
} else if (mappingMatch && firstHostMapping->begin() > guestMapping.begin() && lastHostMapping.end() > lastGuestMapping.end()) {
|
||||
// We've gotten a guest buffer that is located entirely within a host buffer
|
||||
std::scoped_lock bufferLock(*hostMapping->buffer);
|
||||
return hostMapping->buffer->GetView(hostMapping->offset + static_cast<vk::DeviceSize>(hostMapping->begin() - guestMapping.begin()), guest.BufferSize(), guest.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Handle overlapping buffers
|
||||
// Create a list of all overlapping buffers and update the guest mappings to fit them all
|
||||
boost::container::small_vector<std::pair<std::shared_ptr<Buffer>, u32>, 4> overlappingBuffers;
|
||||
GuestBuffer::Mappings newMappings;
|
||||
|
||||
auto guestMappingIt{guest.mappings.begin()};
|
||||
while (true) {
|
||||
do {
|
||||
hostMapping->begin();
|
||||
overlappingBuffers.emplace_back(hostMapping->buffer, 4);
|
||||
} while (hostMapping != buffers.begin() && (--hostMapping)->end() > guestMappingIt->begin());
|
||||
|
||||
// Iterate over all guest mappings to find overlapping buffers, not just the first
|
||||
auto nextGuestMappingIt{std::next(guestMappingIt)};
|
||||
if (nextGuestMappingIt != guest.mappings.end())
|
||||
hostMapping = std::upper_bound(buffers.begin(), buffers.end(), *nextGuestMappingIt);
|
||||
else
|
||||
break;
|
||||
guestMappingIt = nextGuestMappingIt;
|
||||
}
|
||||
|
||||
// Create a buffer that can contain all the overlapping buffers
|
||||
auto buffer{std::make_shared<Buffer>(gpu, guest)};
|
||||
|
||||
// Delete mappings from all overlapping buffers and repoint all buffer views
|
||||
for (auto &overlappingBuffer : overlappingBuffers) {
|
||||
std::scoped_lock overlappingBufferLock(*overlappingBuffer.first);
|
||||
auto &bufferMappings{hostMapping->buffer->guest.mappings};
|
||||
|
||||
// Delete all mappings of the overlapping buffers
|
||||
while ((++it) != buffer->guest.mappings.end()) {
|
||||
guestMapping = *it;
|
||||
auto mapping{std::upper_bound(buffers.begin(), buffers.end(), guestMapping)};
|
||||
buffers.emplace(mapping, BufferMapping{buffer, it, offset, guestMapping});
|
||||
offset += mapping->size_bytes();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
auto buffer{std::make_shared<Buffer>(gpu, guest)};
|
||||
auto it{buffer->guest.mappings.begin()};
|
||||
buffers.emplace(mappingEnd, BufferMapping{buffer, it, 0, guestMapping});
|
||||
|
||||
vk::DeviceSize offset{};
|
||||
while ((++it) != buffer->guest.mappings.end()) {
|
||||
guestMapping = *it;
|
||||
auto mapping{std::upper_bound(buffers.begin(), buffers.end(), guestMapping)};
|
||||
buffers.emplace(mapping, BufferMapping{buffer, it, offset, guestMapping});
|
||||
offset += mapping->size_bytes();
|
||||
}
|
||||
|
||||
return buffer->GetView(0, buffer->size, guest.format);
|
||||
}
|
||||
}
|
42
app/src/main/cpp/skyline/gpu/buffer_manager.h
Normal file
42
app/src/main/cpp/skyline/gpu/buffer_manager.h
Normal file
@ -0,0 +1,42 @@
|
||||
// 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 {
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @brief A single contiguous mapping of a buffer in the CPU address space
|
||||
*/
|
||||
struct BufferMapping : span<u8> {
|
||||
std::shared_ptr<Buffer> buffer;
|
||||
GuestBuffer::Mappings::iterator iterator; //!< An iterator to the mapping in the buffer's GuestBufferMappings corresponding to this mapping
|
||||
vk::DeviceSize offset; //!< Offset of this mapping relative to the start of the buffer
|
||||
|
||||
template<typename... Args>
|
||||
BufferMapping(std::shared_ptr<Buffer> buffer, GuestBuffer::Mappings::iterator iterator, vk::DeviceSize offset, Args &&... args)
|
||||
: span<u8>(std::forward<Args>(args)...),
|
||||
buffer(std::move(buffer)),
|
||||
iterator(iterator),
|
||||
offset(offset) {}
|
||||
};
|
||||
|
||||
GPU &gpu;
|
||||
std::mutex mutex; //!< Synchronizes access to the buffer mappings
|
||||
std::vector<BufferMapping> buffers; //!< A sorted vector of all buffer mappings
|
||||
|
||||
public:
|
||||
BufferManager(GPU &gpu);
|
||||
|
||||
/**
|
||||
* @return A pre-existing or newly created Buffer object which covers the supplied mappings
|
||||
*/
|
||||
std::shared_ptr<BufferView> FindOrCreate(const GuestBuffer &guest);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user