From 6f85a66151d5aea22fef2b6bfe97c5f5d28bbb63 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Tue, 12 Apr 2022 20:50:20 +0530 Subject: [PATCH] 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. --- app/src/main/cpp/skyline/gpu/buffer.cpp | 22 +++++++++++-------- app/src/main/cpp/skyline/gpu/buffer.h | 8 ++++++- .../main/cpp/skyline/gpu/buffer_manager.cpp | 18 +++++++-------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/app/src/main/cpp/skyline/gpu/buffer.cpp b/app/src/main/cpp/skyline/gpu/buffer.cpp index 33685229..84668b0e 100644 --- a/app/src/main/cpp/skyline/gpu/buffer.cpp +++ b/app/src/main/cpp/skyline/gpu/buffer.cpp @@ -9,13 +9,13 @@ namespace skyline::gpu { void Buffer::SetupGuestMappings() { - u8 *alignedData{util::AlignDown(guest.data(), PAGE_SIZE)}; - size_t alignedSize{static_cast(util::AlignUp(guest.data() + guest.size(), PAGE_SIZE) - alignedData)}; + u8 *alignedData{util::AlignDown(guest->data(), PAGE_SIZE)}; + size_t alignedSize{static_cast(util::AlignUp(guest->data() + guest->size(), PAGE_SIZE) - alignedData)}; alignedMirror = gpu.state.process->memory.CreateMirror(alignedData, alignedSize); - mirror = alignedMirror.subspan(static_cast(guest.data() - alignedData), guest.size()); + mirror = alignedMirror.subspan(static_cast(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); SynchronizeGuest(true); // We can skip trapping since the caller will do it WaitOnFence(); @@ -31,6 +31,10 @@ namespace skyline::gpu { 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() { std::lock_guard lock(*this); if (trapHandle) @@ -58,8 +62,8 @@ namespace skyline::gpu { } void Buffer::SynchronizeHost(bool rwTrap) { - if (dirtyState != DirtyState::CpuDirty) - return; // If the buffer has not been modified on the CPU, there is no need to synchronize it + if (dirtyState != DirtyState::CpuDirty || !guest) + 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(); @@ -77,7 +81,7 @@ namespace skyline::gpu { } void Buffer::SynchronizeHostWithCycle(const std::shared_ptr &pCycle, bool rwTrap) { - if (dirtyState != DirtyState::CpuDirty) + if (dirtyState != DirtyState::CpuDirty || !guest) return; if (!cycle.owner_before(pCycle)) @@ -97,8 +101,8 @@ namespace skyline::gpu { } void Buffer::SynchronizeGuest(bool skipTrap, bool skipFence) { - if (dirtyState != DirtyState::GpuDirty) - return; // If the buffer has not been used on the GPU, there is no need to synchronize it + if (dirtyState != DirtyState::GpuDirty || !guest) + 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) WaitOnFence(); diff --git a/app/src/main/cpp/skyline/gpu/buffer.h b/app/src/main/cpp/skyline/gpu/buffer.h index 3d774630..b9a7b9d4 100644 --- a/app/src/main/cpp/skyline/gpu/buffer.h +++ b/app/src/main/cpp/skyline/gpu/buffer.h @@ -21,7 +21,7 @@ namespace skyline::gpu { GPU &gpu; std::mutex mutex; //!< Synchronizes any mutations to the buffer or its backing memory::Buffer backing; - GuestBuffer guest; + std::optional guest; span mirror{}; //!< A contiguous mirror of all the guest mappings to allow linear access on the CPU span 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); + /** + * @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(); /** diff --git a/app/src/main/cpp/skyline/gpu/buffer_manager.cpp b/app/src/main/cpp/skyline/gpu/buffer_manager.cpp index 3866a84f..e64d70e3 100644 --- a/app/src/main/cpp/skyline/gpu/buffer_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/buffer_manager.cpp @@ -9,7 +9,7 @@ namespace skyline::gpu { BufferManager::BufferManager(GPU &gpu) : gpu(gpu) {} bool BufferManager::BufferLessThan(const std::shared_ptr &it, u8 *pointer) { - return it->guest.begin().base() < pointer; + return it->guest->begin().base() < pointer; } BufferView BufferManager::FindOrCreate(GuestBuffer guestMapping, const std::shared_ptr &cycle) { @@ -26,23 +26,23 @@ namespace skyline::gpu { // Lookup for any buffers overlapping with the supplied guest mapping boost::container::small_vector, 4> overlaps; - 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()) + 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()) overlaps.push_back(*entryIt); if (overlaps.size() == 1) [[likely]] { 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 std::scoped_lock bufferLock{*buffer}; - return buffer->GetView(static_cast(guestMapping.begin() - buffer->guest.begin()) + offset, size); + return buffer->GetView(static_cast(guestMapping.begin() - buffer->guest->begin()) + offset, size); } } // 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()}; for (const auto &overlap : overlaps) { - auto mapping{overlap->guest}; + auto mapping{*overlap->guest}; if (mapping.begin().base() < lowestAddress) lowestAddress = mapping.begin().base(); if (mapping.end().base() > highestAddress) @@ -60,7 +60,7 @@ namespace skyline::gpu { 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 - vk::DeviceSize overlapOffset{static_cast(overlap->guest.begin() - newBuffer->guest.begin())}; + vk::DeviceSize overlapOffset{static_cast(overlap->guest->begin() - newBuffer->guest->begin())}; if (overlapOffset != 0) for (auto &view : overlap->views) view.offset += overlapOffset; @@ -77,8 +77,8 @@ namespace skyline::gpu { 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(guestMapping.begin() - newBuffer->guest.begin()) + offset, size); + return newBuffer->GetView(static_cast(guestMapping.begin() - newBuffer->guest->begin()) + offset, size); } }