|
@@ -15,6 +15,7 @@
|
|
|
#include <vector>
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <boost/container/small_vector.hpp>
|
|
#include <boost/container/small_vector.hpp>
|
|
|
|
|
+#include <boost/icl/interval_set.hpp>
|
|
|
|
|
|
|
|
#include "common/common_types.h"
|
|
#include "common/common_types.h"
|
|
|
#include "common/div_ceil.h"
|
|
#include "common/div_ceil.h"
|
|
@@ -77,6 +78,9 @@ class BufferCache {
|
|
|
using Runtime = typename P::Runtime;
|
|
using Runtime = typename P::Runtime;
|
|
|
using Buffer = typename P::Buffer;
|
|
using Buffer = typename P::Buffer;
|
|
|
|
|
|
|
|
|
|
+ using IntervalSet = boost::icl::interval_set<VAddr>;
|
|
|
|
|
+ using IntervalType = typename IntervalSet::interval_type;
|
|
|
|
|
+
|
|
|
struct Empty {};
|
|
struct Empty {};
|
|
|
|
|
|
|
|
struct OverlapResult {
|
|
struct OverlapResult {
|
|
@@ -148,11 +152,14 @@ public:
|
|
|
/// Return true when there are uncommitted buffers to be downloaded
|
|
/// Return true when there are uncommitted buffers to be downloaded
|
|
|
[[nodiscard]] bool HasUncommittedFlushes() const noexcept;
|
|
[[nodiscard]] bool HasUncommittedFlushes() const noexcept;
|
|
|
|
|
|
|
|
|
|
+ void AccumulateFlushes();
|
|
|
|
|
+
|
|
|
/// Return true when the caller should wait for async downloads
|
|
/// Return true when the caller should wait for async downloads
|
|
|
[[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
|
|
[[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
|
|
|
|
|
|
|
|
/// Commit asynchronous downloads
|
|
/// Commit asynchronous downloads
|
|
|
void CommitAsyncFlushes();
|
|
void CommitAsyncFlushes();
|
|
|
|
|
+ void CommitAsyncFlushesHigh();
|
|
|
|
|
|
|
|
/// Pop asynchronous downloads
|
|
/// Pop asynchronous downloads
|
|
|
void PopAsyncFlushes();
|
|
void PopAsyncFlushes();
|
|
@@ -160,6 +167,9 @@ public:
|
|
|
/// Return true when a CPU region is modified from the GPU
|
|
/// Return true when a CPU region is modified from the GPU
|
|
|
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
|
|
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
|
|
|
|
|
|
|
|
|
|
+ /// Return true when a CPU region is modified from the CPU
|
|
|
|
|
+ [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
|
|
|
|
|
+
|
|
|
std::mutex mutex;
|
|
std::mutex mutex;
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
@@ -272,8 +282,6 @@ private:
|
|
|
|
|
|
|
|
void DeleteBuffer(BufferId buffer_id);
|
|
void DeleteBuffer(BufferId buffer_id);
|
|
|
|
|
|
|
|
- void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
|
|
|
|
|
-
|
|
|
|
|
void NotifyBufferDeletion();
|
|
void NotifyBufferDeletion();
|
|
|
|
|
|
|
|
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
|
|
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
|
|
@@ -327,9 +335,9 @@ private:
|
|
|
|
|
|
|
|
std::vector<BufferId> cached_write_buffer_ids;
|
|
std::vector<BufferId> cached_write_buffer_ids;
|
|
|
|
|
|
|
|
- // TODO: This data structure is not optimal and it should be reworked
|
|
|
|
|
- std::vector<BufferId> uncommitted_downloads;
|
|
|
|
|
- std::deque<std::vector<BufferId>> committed_downloads;
|
|
|
|
|
|
|
+ IntervalSet uncommitted_ranges;
|
|
|
|
|
+ IntervalSet common_ranges;
|
|
|
|
|
+ std::deque<IntervalSet> committed_ranges;
|
|
|
|
|
|
|
|
size_t immediate_buffer_capacity = 0;
|
|
size_t immediate_buffer_capacity = 0;
|
|
|
std::unique_ptr<u8[]> immediate_buffer_alloc;
|
|
std::unique_ptr<u8[]> immediate_buffer_alloc;
|
|
@@ -352,6 +360,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
|
|
// Ensure the first slot is used for the null buffer
|
|
// Ensure the first slot is used for the null buffer
|
|
|
void(slot_buffers.insert(runtime, NullBufferParams{}));
|
|
void(slot_buffers.insert(runtime, NullBufferParams{}));
|
|
|
deletion_iterator = slot_buffers.end();
|
|
deletion_iterator = slot_buffers.end();
|
|
|
|
|
+ common_ranges.clear();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
@@ -547,29 +556,30 @@ void BufferCache<P>::FlushCachedWrites() {
|
|
|
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
|
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
|
|
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
|
|
|
- return !uncommitted_downloads.empty();
|
|
|
|
|
|
|
+ return !uncommitted_ranges.empty() || !committed_ranges.empty();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
|
-bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
|
|
|
|
- return !committed_downloads.empty() && !committed_downloads.front().empty();
|
|
|
|
|
|
|
+void BufferCache<P>::AccumulateFlushes() {
|
|
|
|
|
+ if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) {
|
|
|
|
|
+ uncommitted_ranges.clear();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (uncommitted_ranges.empty()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ committed_ranges.emplace_back(std::move(uncommitted_ranges));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
|
-void BufferCache<P>::CommitAsyncFlushes() {
|
|
|
|
|
- // This is intentionally passing the value by copy
|
|
|
|
|
- committed_downloads.push_front(uncommitted_downloads);
|
|
|
|
|
- uncommitted_downloads.clear();
|
|
|
|
|
|
|
+bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
|
-void BufferCache<P>::PopAsyncFlushes() {
|
|
|
|
|
- if (committed_downloads.empty()) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); });
|
|
|
|
|
- const std::span<const BufferId> download_ids = committed_downloads.back();
|
|
|
|
|
- if (download_ids.empty()) {
|
|
|
|
|
|
|
+void BufferCache<P>::CommitAsyncFlushesHigh() {
|
|
|
|
|
+ AccumulateFlushes();
|
|
|
|
|
+ if (committed_ranges.empty()) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
MICROPROFILE_SCOPE(GPU_DownloadMemory);
|
|
MICROPROFILE_SCOPE(GPU_DownloadMemory);
|
|
@@ -577,20 +587,66 @@ void BufferCache<P>::PopAsyncFlushes() {
|
|
|
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
|
|
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
|
|
|
u64 total_size_bytes = 0;
|
|
u64 total_size_bytes = 0;
|
|
|
u64 largest_copy = 0;
|
|
u64 largest_copy = 0;
|
|
|
- for (const BufferId buffer_id : download_ids) {
|
|
|
|
|
- slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) {
|
|
|
|
|
- downloads.push_back({
|
|
|
|
|
- BufferCopy{
|
|
|
|
|
- .src_offset = range_offset,
|
|
|
|
|
- .dst_offset = total_size_bytes,
|
|
|
|
|
- .size = range_size,
|
|
|
|
|
- },
|
|
|
|
|
- buffer_id,
|
|
|
|
|
|
|
+ for (const IntervalSet& intervals : committed_ranges) {
|
|
|
|
|
+ for (auto& interval : intervals) {
|
|
|
|
|
+ const std::size_t size = interval.upper() - interval.lower();
|
|
|
|
|
+ const VAddr cpu_addr = interval.lower();
|
|
|
|
|
+ ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
|
|
|
|
+ boost::container::small_vector<BufferCopy, 1> copies;
|
|
|
|
|
+ buffer.ForEachDownloadRangeAndClear(
|
|
|
|
|
+ cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
|
|
|
|
+ const VAddr buffer_addr = buffer.CpuAddr();
|
|
|
|
|
+ const auto add_download = [&](VAddr start, VAddr end) {
|
|
|
|
|
+ const u64 new_offset = start - buffer_addr;
|
|
|
|
|
+ const u64 new_size = end - start;
|
|
|
|
|
+ downloads.push_back({
|
|
|
|
|
+ BufferCopy{
|
|
|
|
|
+ .src_offset = new_offset,
|
|
|
|
|
+ .dst_offset = total_size_bytes,
|
|
|
|
|
+ .size = new_size,
|
|
|
|
|
+ },
|
|
|
|
|
+ buffer_id,
|
|
|
|
|
+ });
|
|
|
|
|
+ // Align up to avoid cache conflicts
|
|
|
|
|
+ constexpr u64 align = 256ULL;
|
|
|
|
|
+ constexpr u64 mask = ~(align - 1ULL);
|
|
|
|
|
+ total_size_bytes += (new_size + align - 1) & mask;
|
|
|
|
|
+ largest_copy = std::max(largest_copy, new_size);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const VAddr start_address = buffer_addr + range_offset;
|
|
|
|
|
+ const VAddr end_address = start_address + range_size;
|
|
|
|
|
+ const IntervalType search_interval{cpu_addr, 1};
|
|
|
|
|
+ auto it = common_ranges.lower_bound(search_interval);
|
|
|
|
|
+ if (it == common_ranges.end()) {
|
|
|
|
|
+ it = common_ranges.begin();
|
|
|
|
|
+ }
|
|
|
|
|
+ while (it != common_ranges.end()) {
|
|
|
|
|
+ VAddr inter_addr_end = it->upper();
|
|
|
|
|
+ VAddr inter_addr = it->lower();
|
|
|
|
|
+ if (inter_addr >= end_address) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inter_addr_end <= start_address) {
|
|
|
|
|
+ it++;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inter_addr_end > end_address) {
|
|
|
|
|
+ inter_addr_end = end_address;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inter_addr < start_address) {
|
|
|
|
|
+ inter_addr = start_address;
|
|
|
|
|
+ }
|
|
|
|
|
+ add_download(inter_addr, inter_addr_end);
|
|
|
|
|
+ it++;
|
|
|
|
|
+ }
|
|
|
|
|
+ const IntervalType subtract_interval{start_address, end_address};
|
|
|
|
|
+ common_ranges.subtract(subtract_interval);
|
|
|
|
|
+ });
|
|
|
});
|
|
});
|
|
|
- total_size_bytes += range_size;
|
|
|
|
|
- largest_copy = std::max(largest_copy, range_size);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ committed_ranges.clear();
|
|
|
if (downloads.empty()) {
|
|
if (downloads.empty()) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -622,6 +678,19 @@ void BufferCache<P>::PopAsyncFlushes() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+template <class P>
|
|
|
|
|
+void BufferCache<P>::CommitAsyncFlushes() {
|
|
|
|
|
+ if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) {
|
|
|
|
|
+ CommitAsyncFlushesHigh();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uncommitted_ranges.clear();
|
|
|
|
|
+ committed_ranges.clear();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+template <class P>
|
|
|
|
|
+void BufferCache<P>::PopAsyncFlushes() {}
|
|
|
|
|
+
|
|
|
template <class P>
|
|
template <class P>
|
|
|
bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
|
bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
|
|
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
|
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
|
@@ -641,6 +710,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+template <class P>
|
|
|
|
|
+bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
|
|
|
|
|
+ const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
|
|
|
|
+ for (u64 page = addr >> PAGE_BITS; page < page_end;) {
|
|
|
|
|
+ const BufferId image_id = page_table[page];
|
|
|
|
|
+ if (!image_id) {
|
|
|
|
|
+ ++page;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ Buffer& buffer = slot_buffers[image_id];
|
|
|
|
|
+ if (buffer.IsRegionCpuModified(addr, size)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
|
|
|
|
|
+ page = Common::DivCeil(end_addr, PAGE_SIZE);
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
template <class P>
|
|
template <class P>
|
|
|
void BufferCache<P>::BindHostIndexBuffer() {
|
|
void BufferCache<P>::BindHostIndexBuffer() {
|
|
|
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
|
|
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
|
|
@@ -1010,16 +1098,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
|
|
|
Buffer& buffer = slot_buffers[buffer_id];
|
|
Buffer& buffer = slot_buffers[buffer_id];
|
|
|
buffer.MarkRegionAsGpuModified(cpu_addr, size);
|
|
buffer.MarkRegionAsGpuModified(cpu_addr, size);
|
|
|
|
|
|
|
|
- const bool is_accuracy_high = Settings::IsGPULevelHigh();
|
|
|
|
|
|
|
+ const IntervalType base_interval{cpu_addr, cpu_addr + size};
|
|
|
|
|
+ common_ranges.add(base_interval);
|
|
|
|
|
+
|
|
|
|
|
+ const bool is_accuracy_high =
|
|
|
|
|
+ Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High;
|
|
|
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
|
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
|
|
- if (!is_accuracy_high || !is_async) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) {
|
|
|
|
|
- // Already inserted
|
|
|
|
|
|
|
+ if (!is_async && !is_accuracy_high) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- uncommitted_downloads.push_back(buffer_id);
|
|
|
|
|
|
|
+ uncommitted_ranges.add(base_interval);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
@@ -1103,7 +1191,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
|
|
|
if (!copies.empty()) {
|
|
if (!copies.empty()) {
|
|
|
runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
|
|
runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
|
|
|
}
|
|
}
|
|
|
- ReplaceBufferDownloads(overlap_id, new_buffer_id);
|
|
|
|
|
DeleteBuffer(overlap_id);
|
|
DeleteBuffer(overlap_id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1244,14 +1331,51 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
|
|
|
boost::container::small_vector<BufferCopy, 1> copies;
|
|
boost::container::small_vector<BufferCopy, 1> copies;
|
|
|
u64 total_size_bytes = 0;
|
|
u64 total_size_bytes = 0;
|
|
|
u64 largest_copy = 0;
|
|
u64 largest_copy = 0;
|
|
|
- buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
|
|
|
|
- copies.push_back(BufferCopy{
|
|
|
|
|
- .src_offset = range_offset,
|
|
|
|
|
- .dst_offset = total_size_bytes,
|
|
|
|
|
- .size = range_size,
|
|
|
|
|
- });
|
|
|
|
|
- total_size_bytes += range_size;
|
|
|
|
|
- largest_copy = std::max(largest_copy, range_size);
|
|
|
|
|
|
|
+ buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
|
|
|
|
+ const VAddr buffer_addr = buffer.CpuAddr();
|
|
|
|
|
+ const auto add_download = [&](VAddr start, VAddr end) {
|
|
|
|
|
+ const u64 new_offset = start - buffer_addr;
|
|
|
|
|
+ const u64 new_size = end - start;
|
|
|
|
|
+ copies.push_back(BufferCopy{
|
|
|
|
|
+ .src_offset = new_offset,
|
|
|
|
|
+ .dst_offset = total_size_bytes,
|
|
|
|
|
+ .size = new_size,
|
|
|
|
|
+ });
|
|
|
|
|
+ // Align up to avoid cache conflicts
|
|
|
|
|
+ constexpr u64 align = 256ULL;
|
|
|
|
|
+ constexpr u64 mask = ~(align - 1ULL);
|
|
|
|
|
+ total_size_bytes += (new_size + align - 1) & mask;
|
|
|
|
|
+ largest_copy = std::max(largest_copy, new_size);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const VAddr start_address = buffer_addr + range_offset;
|
|
|
|
|
+ const VAddr end_address = start_address + range_size;
|
|
|
|
|
+ const IntervalType search_interval{start_address - range_size, 1};
|
|
|
|
|
+ auto it = common_ranges.lower_bound(search_interval);
|
|
|
|
|
+ if (it == common_ranges.end()) {
|
|
|
|
|
+ it = common_ranges.begin();
|
|
|
|
|
+ }
|
|
|
|
|
+ while (it != common_ranges.end()) {
|
|
|
|
|
+ VAddr inter_addr_end = it->upper();
|
|
|
|
|
+ VAddr inter_addr = it->lower();
|
|
|
|
|
+ if (inter_addr >= end_address) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inter_addr_end <= start_address) {
|
|
|
|
|
+ it++;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inter_addr_end > end_address) {
|
|
|
|
|
+ inter_addr_end = end_address;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inter_addr < start_address) {
|
|
|
|
|
+ inter_addr = start_address;
|
|
|
|
|
+ }
|
|
|
|
|
+ add_download(inter_addr, inter_addr_end);
|
|
|
|
|
+ it++;
|
|
|
|
|
+ }
|
|
|
|
|
+ const IntervalType subtract_interval{start_address, end_address};
|
|
|
|
|
+ common_ranges.subtract(subtract_interval);
|
|
|
});
|
|
});
|
|
|
if (total_size_bytes == 0) {
|
|
if (total_size_bytes == 0) {
|
|
|
return;
|
|
return;
|
|
@@ -1315,18 +1439,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
|
|
|
NotifyBufferDeletion();
|
|
NotifyBufferDeletion();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-template <class P>
|
|
|
|
|
-void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) {
|
|
|
|
|
- const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) {
|
|
|
|
|
- std::ranges::replace(buffers, old_buffer_id, new_buffer_id);
|
|
|
|
|
- if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) {
|
|
|
|
|
- buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end());
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- replace(uncommitted_downloads);
|
|
|
|
|
- std::ranges::for_each(committed_downloads, replace);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
template <class P>
|
|
template <class P>
|
|
|
void BufferCache<P>::NotifyBufferDeletion() {
|
|
void BufferCache<P>::NotifyBufferDeletion() {
|
|
|
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
|
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
|
@@ -1349,15 +1461,9 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
|
|
|
if (!cpu_addr || size == 0) {
|
|
if (!cpu_addr || size == 0) {
|
|
|
return NULL_BINDING;
|
|
return NULL_BINDING;
|
|
|
}
|
|
}
|
|
|
- // HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range.
|
|
|
|
|
- // It exists due to some games like Astral Chain operate out of bounds.
|
|
|
|
|
- // Binding the whole map range would be technically correct, but games have large maps that make
|
|
|
|
|
- // this approach unaffordable for now.
|
|
|
|
|
- static constexpr u32 arbitrary_extra_bytes = 0xc000;
|
|
|
|
|
- const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr));
|
|
|
|
|
const Binding binding{
|
|
const Binding binding{
|
|
|
.cpu_addr = *cpu_addr,
|
|
.cpu_addr = *cpu_addr,
|
|
|
- .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end),
|
|
|
|
|
|
|
+ .size = size,
|
|
|
.buffer_id = BufferId{},
|
|
.buffer_id = BufferId{},
|
|
|
};
|
|
};
|
|
|
return binding;
|
|
return binding;
|