mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-29 15:45:29 +03:00
Implement host-only Buffer
s
We require certain buffers to only be on the host while being accessible through the same abstractions as a guest buffer as they must be interchangeable in usage.
This commit is contained in:
parent
2c697ec36a
commit
6f85a66151
@ -9,13 +9,13 @@
|
|||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
void Buffer::SetupGuestMappings() {
|
void Buffer::SetupGuestMappings() {
|
||||||
u8 *alignedData{util::AlignDown(guest.data(), PAGE_SIZE)};
|
u8 *alignedData{util::AlignDown(guest->data(), PAGE_SIZE)};
|
||||||
size_t alignedSize{static_cast<size_t>(util::AlignUp(guest.data() + guest.size(), PAGE_SIZE) - alignedData)};
|
size_t alignedSize{static_cast<size_t>(util::AlignUp(guest->data() + guest->size(), PAGE_SIZE) - alignedData)};
|
||||||
|
|
||||||
alignedMirror = gpu.state.process->memory.CreateMirror(alignedData, alignedSize);
|
alignedMirror = gpu.state.process->memory.CreateMirror(alignedData, alignedSize);
|
||||||
mirror = alignedMirror.subspan(static_cast<size_t>(guest.data() - alignedData), guest.size());
|
mirror = alignedMirror.subspan(static_cast<size_t>(guest->data() - alignedData), guest->size());
|
||||||
|
|
||||||
trapHandle = gpu.state.nce->TrapRegions(guest, true, [this] {
|
trapHandle = gpu.state.nce->TrapRegions(*guest, true, [this] {
|
||||||
std::lock_guard lock(*this);
|
std::lock_guard lock(*this);
|
||||||
SynchronizeGuest(true); // We can skip trapping since the caller will do it
|
SynchronizeGuest(true); // We can skip trapping since the caller will do it
|
||||||
WaitOnFence();
|
WaitOnFence();
|
||||||
@ -31,6 +31,10 @@ namespace skyline::gpu {
|
|||||||
SetupGuestMappings();
|
SetupGuestMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(GPU &gpu, vk::DeviceSize size) : gpu(gpu), backing(gpu.memory.AllocateBuffer(size)) {
|
||||||
|
dirtyState = DirtyState::Clean; // Since this is a host-only buffer it's always going to be clean
|
||||||
|
}
|
||||||
|
|
||||||
Buffer::~Buffer() {
|
Buffer::~Buffer() {
|
||||||
std::lock_guard lock(*this);
|
std::lock_guard lock(*this);
|
||||||
if (trapHandle)
|
if (trapHandle)
|
||||||
@ -58,8 +62,8 @@ namespace skyline::gpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::SynchronizeHost(bool rwTrap) {
|
void Buffer::SynchronizeHost(bool rwTrap) {
|
||||||
if (dirtyState != DirtyState::CpuDirty)
|
if (dirtyState != DirtyState::CpuDirty || !guest)
|
||||||
return; // If the buffer has not been modified on the CPU, there is no need to synchronize it
|
return; // If the buffer has not been modified on the CPU or there's no guest buffer, there is no need to synchronize it
|
||||||
|
|
||||||
WaitOnFence();
|
WaitOnFence();
|
||||||
|
|
||||||
@ -77,7 +81,7 @@ namespace skyline::gpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::SynchronizeHostWithCycle(const std::shared_ptr<FenceCycle> &pCycle, bool rwTrap) {
|
void Buffer::SynchronizeHostWithCycle(const std::shared_ptr<FenceCycle> &pCycle, bool rwTrap) {
|
||||||
if (dirtyState != DirtyState::CpuDirty)
|
if (dirtyState != DirtyState::CpuDirty || !guest)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!cycle.owner_before(pCycle))
|
if (!cycle.owner_before(pCycle))
|
||||||
@ -97,8 +101,8 @@ namespace skyline::gpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::SynchronizeGuest(bool skipTrap, bool skipFence) {
|
void Buffer::SynchronizeGuest(bool skipTrap, bool skipFence) {
|
||||||
if (dirtyState != DirtyState::GpuDirty)
|
if (dirtyState != DirtyState::GpuDirty || !guest)
|
||||||
return; // If the buffer has not been used on the GPU, there is no need to synchronize it
|
return; // If the buffer has not been used on the GPU or there's no guest buffer, there is no need to synchronize it
|
||||||
|
|
||||||
if (!skipFence)
|
if (!skipFence)
|
||||||
WaitOnFence();
|
WaitOnFence();
|
||||||
|
@ -21,7 +21,7 @@ namespace skyline::gpu {
|
|||||||
GPU &gpu;
|
GPU &gpu;
|
||||||
std::mutex mutex; //!< Synchronizes any mutations to the buffer or its backing
|
std::mutex mutex; //!< Synchronizes any mutations to the buffer or its backing
|
||||||
memory::Buffer backing;
|
memory::Buffer backing;
|
||||||
GuestBuffer guest;
|
std::optional<GuestBuffer> guest;
|
||||||
|
|
||||||
span<u8> mirror{}; //!< A contiguous mirror of all the guest mappings to allow linear access on the CPU
|
span<u8> mirror{}; //!< A contiguous mirror of all the guest mappings to allow linear access on the CPU
|
||||||
span<u8> alignedMirror{}; //!< The mirror mapping aligned to page size to reflect the full mapping
|
span<u8> alignedMirror{}; //!< The mirror mapping aligned to page size to reflect the full mapping
|
||||||
@ -89,6 +89,12 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
Buffer(GPU &gpu, GuestBuffer guest);
|
Buffer(GPU &gpu, GuestBuffer guest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a host-only Buffer which isn't backed by any guest buffer
|
||||||
|
* @note The created buffer won't have a mirror so any operations cannot depend on a mirror existing
|
||||||
|
*/
|
||||||
|
Buffer(GPU &gpu, vk::DeviceSize size);
|
||||||
|
|
||||||
~Buffer();
|
~Buffer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,7 @@ namespace skyline::gpu {
|
|||||||
BufferManager::BufferManager(GPU &gpu) : gpu(gpu) {}
|
BufferManager::BufferManager(GPU &gpu) : gpu(gpu) {}
|
||||||
|
|
||||||
bool BufferManager::BufferLessThan(const std::shared_ptr<Buffer> &it, u8 *pointer) {
|
bool BufferManager::BufferLessThan(const std::shared_ptr<Buffer> &it, u8 *pointer) {
|
||||||
return it->guest.begin().base() < pointer;
|
return it->guest->begin().base() < pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferView BufferManager::FindOrCreate(GuestBuffer guestMapping, const std::shared_ptr<FenceCycle> &cycle) {
|
BufferView BufferManager::FindOrCreate(GuestBuffer guestMapping, const std::shared_ptr<FenceCycle> &cycle) {
|
||||||
@ -26,23 +26,23 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
// Lookup for any buffers overlapping with the supplied guest mapping
|
// Lookup for any buffers overlapping with the supplied guest mapping
|
||||||
boost::container::small_vector<std::shared_ptr<Buffer>, 4> overlaps;
|
boost::container::small_vector<std::shared_ptr<Buffer>, 4> overlaps;
|
||||||
for (auto entryIt{std::lower_bound(buffers.begin(), buffers.end(), guestMapping.end().base(), BufferLessThan)}; entryIt != buffers.begin() && (*--entryIt)->guest.begin() <= guestMapping.end();)
|
for (auto entryIt{std::lower_bound(buffers.begin(), buffers.end(), guestMapping.end().base(), BufferLessThan)}; entryIt != buffers.begin() && (*--entryIt)->guest->begin() <= guestMapping.end();)
|
||||||
if ((*entryIt)->guest.end() > guestMapping.begin())
|
if ((*entryIt)->guest->end() > guestMapping.begin())
|
||||||
overlaps.push_back(*entryIt);
|
overlaps.push_back(*entryIt);
|
||||||
|
|
||||||
if (overlaps.size() == 1) [[likely]] {
|
if (overlaps.size() == 1) [[likely]] {
|
||||||
auto buffer{overlaps.front()};
|
auto buffer{overlaps.front()};
|
||||||
if (buffer->guest.begin() <= guestMapping.begin() && buffer->guest.end() >= guestMapping.end()) {
|
if (buffer->guest->begin() <= guestMapping.begin() && buffer->guest->end() >= guestMapping.end()) {
|
||||||
// If we find a buffer which can entirely fit the guest mapping, we can just return a view into it
|
// If we find a buffer which can entirely fit the guest mapping, we can just return a view into it
|
||||||
std::scoped_lock bufferLock{*buffer};
|
std::scoped_lock bufferLock{*buffer};
|
||||||
return buffer->GetView(static_cast<vk::DeviceSize>(guestMapping.begin() - buffer->guest.begin()) + offset, size);
|
return buffer->GetView(static_cast<vk::DeviceSize>(guestMapping.begin() - buffer->guest->begin()) + offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the extents of the new buffer we want to create that can hold all overlapping buffers
|
// Find the extents of the new buffer we want to create that can hold all overlapping buffers
|
||||||
auto lowestAddress{guestMapping.begin().base()}, highestAddress{guestMapping.end().base()};
|
auto lowestAddress{guestMapping.begin().base()}, highestAddress{guestMapping.end().base()};
|
||||||
for (const auto &overlap : overlaps) {
|
for (const auto &overlap : overlaps) {
|
||||||
auto mapping{overlap->guest};
|
auto mapping{*overlap->guest};
|
||||||
if (mapping.begin().base() < lowestAddress)
|
if (mapping.begin().base() < lowestAddress)
|
||||||
lowestAddress = mapping.begin().base();
|
lowestAddress = mapping.begin().base();
|
||||||
if (mapping.end().base() > highestAddress)
|
if (mapping.end().base() > highestAddress)
|
||||||
@ -60,7 +60,7 @@ namespace skyline::gpu {
|
|||||||
buffers.erase(std::find(buffers.begin(), buffers.end(), overlap));
|
buffers.erase(std::find(buffers.begin(), buffers.end(), overlap));
|
||||||
|
|
||||||
// Transfer all views from the overlapping buffer to the new buffer with the new buffer and updated offset
|
// Transfer all views from the overlapping buffer to the new buffer with the new buffer and updated offset
|
||||||
vk::DeviceSize overlapOffset{static_cast<vk::DeviceSize>(overlap->guest.begin() - newBuffer->guest.begin())};
|
vk::DeviceSize overlapOffset{static_cast<vk::DeviceSize>(overlap->guest->begin() - newBuffer->guest->begin())};
|
||||||
if (overlapOffset != 0)
|
if (overlapOffset != 0)
|
||||||
for (auto &view : overlap->views)
|
for (auto &view : overlap->views)
|
||||||
view.offset += overlapOffset;
|
view.offset += overlapOffset;
|
||||||
@ -77,8 +77,8 @@ namespace skyline::gpu {
|
|||||||
newBuffer->delegates.splice(newBuffer->delegates.end(), overlap->delegates);
|
newBuffer->delegates.splice(newBuffer->delegates.end(), overlap->delegates);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers.insert(std::lower_bound(buffers.begin(), buffers.end(), newBuffer->guest.end().base(), BufferLessThan), newBuffer);
|
buffers.insert(std::lower_bound(buffers.begin(), buffers.end(), newBuffer->guest->end().base(), BufferLessThan), newBuffer);
|
||||||
|
|
||||||
return newBuffer->GetView(static_cast<vk::DeviceSize>(guestMapping.begin() - newBuffer->guest.begin()) + offset, size);
|
return newBuffer->GetView(static_cast<vk::DeviceSize>(guestMapping.begin() - newBuffer->guest->begin()) + offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user