mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-10 19:05:08 +01:00
Commonise buffer interval list code
This will be reused for usagetracker.
This commit is contained in:
parent
0949d51871
commit
f64860c93e
119
app/src/main/cpp/skyline/common/interval_list.h
Normal file
119
app/src/main/cpp/skyline/common/interval_list.h
Normal file
@ -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<typename SizeType>
|
||||||
|
class IntervalList {
|
||||||
|
public:
|
||||||
|
using DifferenceType = decltype(std::declval<SizeType>() - std::declval<SizeType>());
|
||||||
|
|
||||||
|
struct Interval {
|
||||||
|
SizeType offset;
|
||||||
|
SizeType end;
|
||||||
|
|
||||||
|
Interval() = default;
|
||||||
|
|
||||||
|
Interval(SizeType offset, SizeType end) : offset{offset}, end{end} {}
|
||||||
|
|
||||||
|
Interval(span<u8> interval) : offset{interval.data()}, end{interval.data() + interval.size()} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Interval> 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<SizeType> &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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -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() {
|
void Buffer::EnableTrackedShadowDirect() {
|
||||||
if (!directTrackedShadowActive) {
|
if (!directTrackedShadowActive) {
|
||||||
directTrackedShadow.resize(guest->size());
|
directTrackedShadow.resize(guest->size());
|
||||||
@ -168,7 +125,7 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
span<u8> Buffer::BeginWriteCpuSequencedDirect(size_t offset, size_t size) {
|
span<u8> Buffer::BeginWriteCpuSequencedDirect(size_t offset, size_t size) {
|
||||||
EnableTrackedShadowDirect();
|
EnableTrackedShadowDirect();
|
||||||
InsertWriteIntervalDirect({offset, offset + size});
|
directTrackedWrites.Insert({offset, offset + size});
|
||||||
return {directTrackedShadow.data() + offset, size};
|
return {directTrackedShadow.data() + offset, size};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +137,7 @@ namespace skyline::gpu {
|
|||||||
directTrackedShadow.clear();
|
directTrackedShadow.clear();
|
||||||
directTrackedShadow.shrink_to_fit();
|
directTrackedShadow.shrink_to_fit();
|
||||||
}
|
}
|
||||||
directTrackedWrites.clear();
|
directTrackedWrites.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return readsActive;
|
return readsActive;
|
||||||
@ -352,8 +309,8 @@ namespace skyline::gpu {
|
|||||||
if (directTrackedShadowActive && RefreshGpuReadsActiveDirect()) {
|
if (directTrackedShadowActive && RefreshGpuReadsActiveDirect()) {
|
||||||
size_t curOffset{offset};
|
size_t curOffset{offset};
|
||||||
while (curOffset != data.size() + offset) {
|
while (curOffset != data.size() + offset) {
|
||||||
auto result{QueryWriteIntervalDirect(curOffset)};
|
auto result{directTrackedWrites.Query(curOffset)};
|
||||||
auto srcData{result.useShadow ? directTrackedShadow.data() : mirror.data()};
|
auto srcData{result.enclosed ? directTrackedShadow.data() : mirror.data()};
|
||||||
std::memcpy(data.data() + curOffset - offset, srcData + curOffset, result.size);
|
std::memcpy(data.data() + curOffset - offset, srcData + curOffset, result.size);
|
||||||
curOffset += result.size;
|
curOffset += result.size;
|
||||||
}
|
}
|
||||||
|
@ -49,14 +49,7 @@ namespace skyline::gpu {
|
|||||||
size_t id;
|
size_t id;
|
||||||
bool isDirect{}; //!< Indicates if a buffer is directly mapped from the guest
|
bool isDirect{}; //!< Indicates if a buffer is directly mapped from the guest
|
||||||
|
|
||||||
/**
|
IntervalList<size_t> directTrackedWrites; //!< (Direct) A list of tracking intervals for the buffer, this is used to determine when to read from `directTrackedShadow`
|
||||||
* @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<WriteTrackingInterval> directTrackedWrites; //!< (Direct) A vector of write tracking intervals for the buffer, this is used to determine when to read from `directTrackedShadow`
|
|
||||||
std::vector<u8> directTrackedShadow; //!< (Direct) Temporary mirror used to track any CPU-side writes to the buffer while it's being read by the GPU
|
std::vector<u8> 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
|
bool directTrackedShadowActive{}; //!< (Direct) If `directTrackedShadow` is currently being used to track writes
|
||||||
|
|
||||||
@ -116,11 +109,6 @@ namespace skyline::gpu {
|
|||||||
void ResetMegabufferState();
|
void ResetMegabufferState();
|
||||||
|
|
||||||
private:
|
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;
|
BufferDelegate *delegate;
|
||||||
|
|
||||||
friend BufferView;
|
friend BufferView;
|
||||||
@ -128,16 +116,6 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
void SetupStagedTraps();
|
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
|
* @brief Enables the shadow buffer used for sequencing buffer contents independently of the GPU on the CPU
|
||||||
*/
|
*/
|
||||||
|
@ -130,8 +130,7 @@ namespace skyline::gpu {
|
|||||||
} else if (srcBuffer->directTrackedShadowActive) {
|
} else if (srcBuffer->directTrackedShadowActive) {
|
||||||
newBuffer->EnableTrackedShadowDirect();
|
newBuffer->EnableTrackedShadowDirect();
|
||||||
copyBuffer(*newBuffer->guest, *srcBuffer->guest, newBuffer->directTrackedShadow.data(), srcBuffer->directTrackedShadow.data());
|
copyBuffer(*newBuffer->guest, *srcBuffer->guest, newBuffer->directTrackedShadow.data(), srcBuffer->directTrackedShadow.data());
|
||||||
for (const auto &interval : srcBuffer->directTrackedWrites)
|
newBuffer->directTrackedWrites.Merge(srcBuffer->directTrackedWrites);
|
||||||
newBuffer->InsertWriteIntervalDirect(interval);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user