From f64860c93ed7231e9c418acabb5941db5a79d63d Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 4 Mar 2023 20:09:38 +0000 Subject: [PATCH] Commonise buffer interval list code This will be reused for usagetracker. --- .../main/cpp/skyline/common/interval_list.h | 119 ++++++++++++++++++ app/src/main/cpp/skyline/gpu/buffer.cpp | 51 +------- app/src/main/cpp/skyline/gpu/buffer.h | 24 +--- .../main/cpp/skyline/gpu/buffer_manager.cpp | 3 +- 4 files changed, 125 insertions(+), 72 deletions(-) create mode 100644 app/src/main/cpp/skyline/common/interval_list.h diff --git a/app/src/main/cpp/skyline/common/interval_list.h b/app/src/main/cpp/skyline/common/interval_list.h new file mode 100644 index 00000000..d9509bfa --- /dev/null +++ b/app/src/main/cpp/skyline/common/interval_list.h @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "base.h" +#include "span.h" + +namespace skyline { + /** + * @brief Stores a list of non-overlapping intervals + */ + template + class IntervalList { + public: + using DifferenceType = decltype(std::declval() - std::declval()); + + struct Interval { + SizeType offset; + SizeType end; + + Interval() = default; + + Interval(SizeType offset, SizeType end) : offset{offset}, end{end} {} + + Interval(span interval) : offset{interval.data()}, end{interval.data() + interval.size()} {} + }; + + private: + std::vector intervals; //!< A list of intervals sorted by their end offset + + public: + struct QueryResult { + bool enclosed; //!< If the given offset was enclosed by an interval + DifferenceType size; //!< Size of the interval starting from the query offset, or distance to the next interval if `enclosed` is false (if there is no next interval size is 0) + }; + + /** + * @brief Clears all inserted intervals from the map + */ + void Clear() { + intervals.clear(); + } + + /** + * @brief Forces future accesses to the given interval to use the shadow copy + */ + void Insert(Interval entry) { + auto firstIt{std::lower_bound(intervals.begin(), intervals.end(), entry, [](const auto &lhs, const auto &rhs) { + return lhs.end < rhs.offset; + })}; // Lowest offset entry that (maybe) overlaps with the new entry + + if (firstIt == intervals.end() || firstIt->offset >= entry.end) { + intervals.insert(firstIt, entry); + return; + } + // Now firstIt will always overlap + + auto lastIt{firstIt}; // Highest offset entry that overlaps with the new entry + while (std::next(lastIt) != intervals.end() && std::next(lastIt)->offset < entry.end) + lastIt++; + + // Since firstIt and lastIt both are guaranteed to overlap, max them to get the new entry's end + SizeType end{std::max(std::max(firstIt->end, entry.end), lastIt->end)}; + + // Erase all overlapping entries but the first + auto eraseStartIt{std::next(firstIt)}; + auto eraseEndIt{std::next(lastIt)}; + if (eraseStartIt != eraseEndIt) { + lastIt = intervals.erase(eraseStartIt, eraseEndIt); + firstIt = std::prev(lastIt); + } + + firstIt->offset = std::min(entry.offset, firstIt->offset); + firstIt->end = end; + } + + /** + * @brief Merge the given interval into the list + */ + void Merge(const IntervalList &list) { + for (auto &entry : list.intervals) + Insert(entry); + } + + /** + * @return A struct describing the interval containing `offset` + */ + QueryResult Query(SizeType offset) { + auto it{std::lower_bound(intervals.begin(), intervals.end(), offset, [](const auto &lhs, const auto &rhs) { + return lhs.end < rhs; + })}; // Lowest offset entry that (maybe) overlaps with the new entry + + if (it == intervals.end()) // No overlaps past offset + return {false, {}}; + else if (it->offset > offset) // No overlap, return the distance to the next possible overlap + return {false, it->offset - offset}; + else // Overlap, return the distance to the end of the overlap + return {true, it->end - offset}; + } + + /** + * @return If the given interval intersects with any of the intervals in the list + */ + bool Intersect(Interval interval) { + SizeType offset{interval.offset}; + while (offset < interval.end) { + if (auto result{Query(offset)}; result.enclosed) + return true; + else if (result.size) + offset += result.size; + else + return false; + } + + return false; + } + }; +} diff --git a/app/src/main/cpp/skyline/gpu/buffer.cpp b/app/src/main/cpp/skyline/gpu/buffer.cpp index 07506fa5..d90fd638 100644 --- a/app/src/main/cpp/skyline/gpu/buffer.cpp +++ b/app/src/main/cpp/skyline/gpu/buffer.cpp @@ -116,49 +116,6 @@ namespace skyline::gpu { }); } - void Buffer::InsertWriteIntervalDirect(WriteTrackingInterval entry) { - auto firstIt{std::lower_bound(directTrackedWrites.begin(), directTrackedWrites.end(), entry, [](const auto &lhs, const auto &rhs) { - return lhs.end < rhs.offset; - })}; // Lowest offset entry that (maybe) overlaps with the new entry - - if (firstIt == directTrackedWrites.end() || firstIt->offset >= entry.end) { - directTrackedWrites.insert(firstIt, entry); - return; - } - // Now firstIt will always overlap - - auto lastIt{firstIt}; // Highest offset entry that overlaps with the new entry - while (std::next(lastIt) != directTrackedWrites.end() && std::next(lastIt)->offset < entry.end) - lastIt++; - - // Since firstIt and lastIt both are guaranteed to overlap, max them to get the new entry's end - size_t end{std::max(std::max(firstIt->end, entry.end), lastIt->end)}; - - // Erase all overlapping entries but the first - auto eraseStartIt{std::next(firstIt)}; - auto eraseEndIt{std::next(lastIt)}; - if (eraseStartIt != eraseEndIt) { - lastIt = directTrackedWrites.erase(eraseStartIt, eraseEndIt); - firstIt = std::prev(lastIt); - } - - firstIt->offset = std::min(entry.offset, firstIt->offset); - firstIt->end = end; - } - - Buffer::QueryIntervalResult Buffer::QueryWriteIntervalDirect(u64 offset) { - auto it{std::lower_bound(directTrackedWrites.begin(), directTrackedWrites.end(), offset, [](const auto &lhs, const auto &rhs) { - return lhs.end < rhs; - })}; // Lowest offset entry that (maybe) overlaps with the new entry - - if (it == directTrackedWrites.end()) // No overlaps for the entire rest of buffer - return {false, mirror.size() - offset}; - else if (it->offset > offset) // No overlap, return the distance to the next possible overlap - return {false, it->offset - offset}; - else // Overlap, return the distance to the end of the overlap - return {true, it->end - offset}; - } - void Buffer::EnableTrackedShadowDirect() { if (!directTrackedShadowActive) { directTrackedShadow.resize(guest->size()); @@ -168,7 +125,7 @@ namespace skyline::gpu { span Buffer::BeginWriteCpuSequencedDirect(size_t offset, size_t size) { EnableTrackedShadowDirect(); - InsertWriteIntervalDirect({offset, offset + size}); + directTrackedWrites.Insert({offset, offset + size}); return {directTrackedShadow.data() + offset, size}; } @@ -180,7 +137,7 @@ namespace skyline::gpu { directTrackedShadow.clear(); directTrackedShadow.shrink_to_fit(); } - directTrackedWrites.clear(); + directTrackedWrites.Clear(); } return readsActive; @@ -352,8 +309,8 @@ namespace skyline::gpu { if (directTrackedShadowActive && RefreshGpuReadsActiveDirect()) { size_t curOffset{offset}; while (curOffset != data.size() + offset) { - auto result{QueryWriteIntervalDirect(curOffset)}; - auto srcData{result.useShadow ? directTrackedShadow.data() : mirror.data()}; + auto result{directTrackedWrites.Query(curOffset)}; + auto srcData{result.enclosed ? directTrackedShadow.data() : mirror.data()}; std::memcpy(data.data() + curOffset - offset, srcData + curOffset, result.size); curOffset += result.size; } diff --git a/app/src/main/cpp/skyline/gpu/buffer.h b/app/src/main/cpp/skyline/gpu/buffer.h index b9bbfd37..b538ad99 100644 --- a/app/src/main/cpp/skyline/gpu/buffer.h +++ b/app/src/main/cpp/skyline/gpu/buffer.h @@ -49,14 +49,7 @@ namespace skyline::gpu { size_t id; bool isDirect{}; //!< Indicates if a buffer is directly mapped from the guest - /** - * @brief Interval struct used to track which part of the buffer should be accessed through the shadow - */ - struct WriteTrackingInterval { - size_t offset; - size_t end; - }; - std::vector directTrackedWrites; //!< (Direct) A vector of write tracking intervals for the buffer, this is used to determine when to read from `directTrackedShadow` + IntervalList directTrackedWrites; //!< (Direct) A list of tracking intervals for the buffer, this is used to determine when to read from `directTrackedShadow` std::vector directTrackedShadow; //!< (Direct) Temporary mirror used to track any CPU-side writes to the buffer while it's being read by the GPU bool directTrackedShadowActive{}; //!< (Direct) If `directTrackedShadow` is currently being used to track writes @@ -116,11 +109,6 @@ namespace skyline::gpu { void ResetMegabufferState(); private: - struct QueryIntervalResult { - bool useShadow; //!< If the shadow should be used for buffer accesses within the interval - u64 size; //!< Size of the interval starting from the query offset - }; - BufferDelegate *delegate; friend BufferView; @@ -128,16 +116,6 @@ namespace skyline::gpu { void SetupStagedTraps(); - /** - * @brief Forces future accesses to the given interval to use the shadow copy - */ - void InsertWriteIntervalDirect(WriteTrackingInterval interval); - - /** - * @return A struct describing the interval containing `offset` - */ - QueryIntervalResult QueryWriteIntervalDirect(u64 offset); - /** * @brief Enables the shadow buffer used for sequencing buffer contents independently of the GPU on the CPU */ diff --git a/app/src/main/cpp/skyline/gpu/buffer_manager.cpp b/app/src/main/cpp/skyline/gpu/buffer_manager.cpp index ce872df4..9d894a92 100644 --- a/app/src/main/cpp/skyline/gpu/buffer_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/buffer_manager.cpp @@ -130,8 +130,7 @@ namespace skyline::gpu { } else if (srcBuffer->directTrackedShadowActive) { newBuffer->EnableTrackedShadowDirect(); copyBuffer(*newBuffer->guest, *srcBuffer->guest, newBuffer->directTrackedShadow.data(), srcBuffer->directTrackedShadow.data()); - for (const auto &interval : srcBuffer->directTrackedWrites) - newBuffer->InsertWriteIntervalDirect(interval); + newBuffer->directTrackedWrites.Merge(srcBuffer->directTrackedWrites); } }