mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 20:06:54 +02:00
renderer: remove global BufferCache locking
This commit is contained in:
+133
-187
@@ -22,30 +22,9 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
thread_local const void* g_cache_lock_owner = nullptr;
|
|
||||||
|
|
||||||
constexpr uint64_t MiB = 1024 * 1024;
|
constexpr uint64_t MiB = 1024 * 1024;
|
||||||
constexpr uint64_t GdsBufferSize = 64 * 1024;
|
constexpr uint64_t GdsBufferSize = 64 * 1024;
|
||||||
|
|
||||||
class FaultSafeCacheLock final {
|
|
||||||
public:
|
|
||||||
FaultSafeCacheLock(const void* owner, Common::Mutex& mutex): m_mutex(mutex) {
|
|
||||||
if (g_cache_lock_owner != nullptr) {
|
|
||||||
EXIT("BufferCache: recursive cache lock acquisition\n");
|
|
||||||
}
|
|
||||||
g_cache_lock_owner = owner;
|
|
||||||
m_mutex.Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
~FaultSafeCacheLock() {
|
|
||||||
m_mutex.Unlock();
|
|
||||||
g_cache_lock_owner = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Common::Mutex& m_mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
uint64_t BufferCache::AlignDown(uint64_t value) noexcept {
|
uint64_t BufferCache::AlignDown(uint64_t value) noexcept {
|
||||||
@@ -191,21 +170,18 @@ void BufferCache::QueueGarbageDownload(std::span<const DownloadCopy> copies, Ret
|
|||||||
m_scheduler.DeferOperation(
|
m_scheduler.DeferOperation(
|
||||||
[this, downloads = std::move(downloads), retire = std::move(retire)]() mutable {
|
[this, downloads = std::move(downloads), retire = std::move(retire)]() mutable {
|
||||||
PublishDownloads(downloads);
|
PublishDownloads(downloads);
|
||||||
{
|
for (const auto& range: downloads) {
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
||||||
for (const auto& range: downloads) {
|
|
||||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
|
||||||
}
|
|
||||||
// ForEachDownloadRange reports full tracker pages, and every exact GPU-owned
|
|
||||||
// interval on those pages was downloaded and removed. Clearing the original
|
|
||||||
// query therefore cannot orphan a dirty sibling on an edge page.
|
|
||||||
m_memory_tracker.UnmarkRegionAsGpuModified(retire.address, retire.size);
|
|
||||||
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
|
|
||||||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
|
|
||||||
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
|
|
||||||
}
|
|
||||||
m_memory_tracker.UntrackMemory(retire.address, retire.size);
|
|
||||||
}
|
}
|
||||||
|
// ForEachDownloadRange reports full tracker pages, and every exact GPU-owned
|
||||||
|
// interval on those pages was downloaded and removed. Clearing the original
|
||||||
|
// query therefore cannot orphan a dirty sibling on an edge page.
|
||||||
|
m_memory_tracker.UnmarkRegionAsGpuModified(retire.address, retire.size);
|
||||||
|
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
|
||||||
|
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
|
||||||
|
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
|
||||||
|
}
|
||||||
|
m_memory_tracker.UntrackMemory(retire.address, retire.size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,9 +252,6 @@ void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
|||||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||||
EXIT("BufferCache: invalid memory-invalidation range\n");
|
EXIT("BufferCache: invalid memory-invalidation range\n");
|
||||||
}
|
}
|
||||||
if (!IsRegionRegistered(vaddr, size)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_memory_tracker.InvalidateRegion(vaddr, size,
|
m_memory_tracker.InvalidateRegion(vaddr, size,
|
||||||
[this, vaddr, size] { ReadMemory(vaddr, size, true); });
|
[this, vaddr, size] { ReadMemory(vaddr, size, true); });
|
||||||
}
|
}
|
||||||
@@ -298,60 +271,58 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write) {
|
void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write) {
|
||||||
|
// CPU invalidation reaches this point only for a GPU-owned tracker page. Resolve the exact
|
||||||
|
// Buffer owner on the GPU thread so the cache index remains single-thread-owned.
|
||||||
|
if (is_write && !IsRegionRegistered(vaddr, size)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
std::vector<DownloadCopy> copies;
|
std::vector<DownloadCopy> copies;
|
||||||
{
|
m_memory_tracker.ForEachDownloadRange<false>(
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
vaddr, size,
|
||||||
m_memory_tracker.ForEachDownloadRange<false>(
|
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||||
vaddr, size,
|
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
"memory invalidation");
|
||||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
},
|
||||||
"memory invalidation");
|
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||||
},
|
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
for (uint64_t copied = 0; copied < range.size;) {
|
||||||
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
const auto copy_address = range.address + copied;
|
||||||
for (uint64_t copied = 0; copied < range.size;) {
|
auto owner = m_buffers.upper_bound(copy_address);
|
||||||
const auto copy_address = range.address + copied;
|
if (owner == m_buffers.begin()) {
|
||||||
auto owner = m_buffers.upper_bound(copy_address);
|
EXIT("BufferCache: invalidation readback has no buffer owner\n");
|
||||||
if (owner == m_buffers.begin()) {
|
|
||||||
EXIT("BufferCache: invalidation readback has no buffer owner\n");
|
|
||||||
}
|
|
||||||
auto& cached = *std::prev(owner)->second;
|
|
||||||
if (!cached.buffer->IsInBounds(copy_address, 1)) {
|
|
||||||
EXIT(
|
|
||||||
"BufferCache: invalidation readback is outside its buffer owner\n");
|
|
||||||
}
|
|
||||||
const auto copy_size = std::min(range.size - copied,
|
|
||||||
cached.vaddr + cached.size - copy_address);
|
|
||||||
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
|
|
||||||
copy_address, copy_size});
|
|
||||||
copied += copy_size;
|
|
||||||
}
|
}
|
||||||
|
auto& cached = *std::prev(owner)->second;
|
||||||
|
if (!cached.buffer->IsInBounds(copy_address, 1)) {
|
||||||
|
EXIT("BufferCache: invalidation readback is outside its buffer owner\n");
|
||||||
|
}
|
||||||
|
const auto copy_size =
|
||||||
|
std::min(range.size - copied, cached.vaddr + cached.size - copy_address);
|
||||||
|
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
|
||||||
|
copy_address, copy_size});
|
||||||
|
copied += copy_size;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (copies.empty()) {
|
});
|
||||||
if (!is_write) {
|
if (copies.empty()) {
|
||||||
return;
|
if (!is_write) {
|
||||||
}
|
|
||||||
// A preceding read fault can consume the last GPU-owned copy after this write
|
|
||||||
// invalidation has already chosen to flush. Complete the CPU ownership handoff even
|
|
||||||
// though this callback no longer has bytes to download.
|
|
||||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// A preceding read fault can consume the last GPU-owned copy after this write invalidation
|
||||||
|
// has already chosen to flush. Complete the CPU ownership handoff even though this callback
|
||||||
|
// no longer has bytes to download.
|
||||||
|
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
auto downloads = RecordDownloads(copies);
|
auto downloads = RecordDownloads(copies);
|
||||||
m_scheduler.FinishCurrent();
|
m_scheduler.FinishCurrent();
|
||||||
PublishDownloads(downloads);
|
PublishDownloads(downloads);
|
||||||
{
|
for (const auto& range: downloads) {
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
||||||
for (const auto& range: downloads) {
|
}
|
||||||
m_gpu_modified_ranges.Subtract(range.address, range.size);
|
// The enumeration above covered whole dirty pages and every exact interval on them.
|
||||||
}
|
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
|
||||||
// The enumeration above covered whole dirty pages and every exact interval on them.
|
if (is_write) {
|
||||||
m_memory_tracker.UnmarkRegionAsGpuModified(vaddr, size);
|
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
||||||
if (is_write) {
|
|
||||||
m_memory_tracker.MarkRegionAsCpuModified(vaddr, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,43 +333,40 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
|||||||
std::vector<DownloadCopy> copies;
|
std::vector<DownloadCopy> copies;
|
||||||
std::vector<std::pair<uint64_t, uint64_t>> modified_buffers;
|
std::vector<std::pair<uint64_t, uint64_t>> modified_buffers;
|
||||||
std::vector<std::pair<uint64_t, uint64_t>> retired_buffers;
|
std::vector<std::pair<uint64_t, uint64_t>> retired_buffers;
|
||||||
{
|
for (const auto& [begin, cached]: m_buffers) {
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
if (vaddr < begin + cached->size && begin < vaddr + size) {
|
||||||
for (const auto& [begin, cached]: m_buffers) {
|
retired_buffers.emplace_back(begin, cached->size);
|
||||||
if (vaddr < begin + cached->size && begin < vaddr + size) {
|
|
||||||
retired_buffers.emplace_back(begin, cached->size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (const auto& [begin, cached]: m_buffers) {
|
}
|
||||||
if (vaddr >= begin + cached->size || begin >= vaddr + size ||
|
for (const auto& [begin, cached]: m_buffers) {
|
||||||
!m_memory_tracker.IsRegionGpuModified(begin, cached->size)) {
|
if (vaddr >= begin + cached->size || begin >= vaddr + size ||
|
||||||
continue;
|
!m_memory_tracker.IsRegionGpuModified(begin, cached->size)) {
|
||||||
}
|
continue;
|
||||||
const auto dirty = m_gpu_modified_ranges.Intersections(begin, cached->size);
|
|
||||||
if (dirty.empty()) {
|
|
||||||
EXIT("BufferCache: GPU-modified buffer has no dirty ranges\n");
|
|
||||||
}
|
|
||||||
modified_buffers.emplace_back(begin, cached->size);
|
|
||||||
}
|
}
|
||||||
for (const auto& [begin, bytes]: modified_buffers) {
|
const auto dirty = m_gpu_modified_ranges.Intersections(begin, cached->size);
|
||||||
auto owner = m_buffers.find(begin);
|
if (dirty.empty()) {
|
||||||
if (owner == m_buffers.end() || owner->second->size != bytes) {
|
EXIT("BufferCache: GPU-modified buffer has no dirty ranges\n");
|
||||||
EXIT("BufferCache: unmap owner changed during collection\n");
|
|
||||||
}
|
|
||||||
auto& cached = *owner->second;
|
|
||||||
m_memory_tracker.ForEachDownloadRange<false>(
|
|
||||||
begin, cached.size,
|
|
||||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
|
||||||
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
|
||||||
"unmap");
|
|
||||||
},
|
|
||||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
|
||||||
for (const auto& range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
|
||||||
copies.push_back(
|
|
||||||
{cached.buffer, range.address - begin, range.address, range.size});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
modified_buffers.emplace_back(begin, cached->size);
|
||||||
|
}
|
||||||
|
for (const auto& [begin, bytes]: modified_buffers) {
|
||||||
|
auto owner = m_buffers.find(begin);
|
||||||
|
if (owner == m_buffers.end() || owner->second->size != bytes) {
|
||||||
|
EXIT("BufferCache: unmap owner changed during collection\n");
|
||||||
|
}
|
||||||
|
auto& cached = *owner->second;
|
||||||
|
m_memory_tracker.ForEachDownloadRange<false>(
|
||||||
|
begin, cached.size,
|
||||||
|
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||||
|
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
|
||||||
|
"unmap");
|
||||||
|
},
|
||||||
|
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||||
|
for (const auto& range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||||
|
copies.push_back(
|
||||||
|
{cached.buffer, range.address - begin, range.address, range.size});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!copies.empty()) {
|
if (!copies.empty()) {
|
||||||
auto downloads = RecordDownloads(copies);
|
auto downloads = RecordDownloads(copies);
|
||||||
@@ -409,29 +377,26 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
|||||||
// command stream before removing such backing.
|
// command stream before removing such backing.
|
||||||
m_scheduler.FinishCurrent();
|
m_scheduler.FinishCurrent();
|
||||||
}
|
}
|
||||||
{
|
for (const auto& [begin, bytes]: modified_buffers) {
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
m_gpu_modified_ranges.Subtract(begin, bytes);
|
||||||
for (const auto& [begin, bytes]: modified_buffers) {
|
m_memory_tracker.UnmarkRegionAsGpuModified(begin, bytes);
|
||||||
m_gpu_modified_ranges.Subtract(begin, bytes);
|
}
|
||||||
m_memory_tracker.UnmarkRegionAsGpuModified(begin, bytes);
|
for (const auto& [begin, bytes]: retired_buffers) {
|
||||||
}
|
m_memory_tracker.MarkRegionAsCpuModified(begin, bytes);
|
||||||
for (const auto& [begin, bytes]: retired_buffers) {
|
}
|
||||||
m_memory_tracker.MarkRegionAsCpuModified(begin, bytes);
|
if (!m_gpu_modified_ranges.Intersections(vaddr, size).empty()) {
|
||||||
}
|
EXIT("BufferCache: unmap retained dirty byte ranges\n");
|
||||||
if (!m_gpu_modified_ranges.Intersections(vaddr, size).empty()) {
|
}
|
||||||
EXIT("BufferCache: unmap retained dirty byte ranges\n");
|
m_memory_tracker.UntrackMemory(vaddr, size);
|
||||||
}
|
for (auto it = m_buffers.begin(); it != m_buffers.end();) {
|
||||||
m_memory_tracker.UntrackMemory(vaddr, size);
|
if (vaddr < it->first + it->second->size && it->first < vaddr + size) {
|
||||||
for (auto it = m_buffers.begin(); it != m_buffers.end();) {
|
if (it->second->size > m_total_used_memory) {
|
||||||
if (vaddr < it->first + it->second->size && it->first < vaddr + size) {
|
EXIT("BufferCache: allocation accounting underflow\n");
|
||||||
if (it->second->size > m_total_used_memory) {
|
|
||||||
EXIT("BufferCache: allocation accounting underflow\n");
|
|
||||||
}
|
|
||||||
m_total_used_memory -= it->second->size;
|
|
||||||
it = m_buffers.erase(it);
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
m_total_used_memory -= it->second->size;
|
||||||
|
it = m_buffers.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -539,29 +504,22 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
|||||||
(void)m_texture_cache.InvalidateMemoryFromGPU(vaddr, size, true);
|
(void)m_texture_cache.InvalidateMemoryFromGPU(vaddr, size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Buffer> owner;
|
auto& cached = GetOrCreateBuffer(command, vaddr, size);
|
||||||
uint64_t offset = 0;
|
std::vector<std::pair<uint64_t, uint64_t>> uploads;
|
||||||
{
|
m_memory_tracker.ForEachUploadRange(
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
vaddr, size, is_written,
|
||||||
auto& cached = GetOrCreateBuffer(command, vaddr, size);
|
[&](uint64_t address, uint64_t bytes) noexcept { uploads.emplace_back(address, bytes); },
|
||||||
std::vector<std::pair<uint64_t, uint64_t>> uploads;
|
[&]() noexcept {
|
||||||
m_memory_tracker.ForEachUploadRange(
|
for (const auto& [address, bytes]: uploads) {
|
||||||
vaddr, size, is_written,
|
Upload(command, *cached.buffer, cached.buffer->Offset(address),
|
||||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
reinterpret_cast<const void*>(address), bytes);
|
||||||
uploads.emplace_back(address, bytes);
|
}
|
||||||
},
|
});
|
||||||
[&]() noexcept {
|
if (is_written) {
|
||||||
for (const auto& [address, bytes]: uploads) {
|
m_gpu_modified_ranges.Add(vaddr, size);
|
||||||
Upload(command, *cached.buffer, cached.buffer->Offset(address),
|
|
||||||
reinterpret_cast<const void*>(address), bytes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (is_written) {
|
|
||||||
m_gpu_modified_ranges.Add(vaddr, size);
|
|
||||||
}
|
|
||||||
owner = cached.buffer;
|
|
||||||
offset = cached.buffer->Offset(vaddr);
|
|
||||||
}
|
}
|
||||||
|
auto owner = cached.buffer;
|
||||||
|
uint64_t offset = cached.buffer->Offset(vaddr);
|
||||||
if (is_formatted && is_read && !is_written) {
|
if (is_formatted && is_read && !is_written) {
|
||||||
(void)SynchronizeBufferFromImage(*owner, vaddr, size);
|
(void)SynchronizeBufferFromImage(*owner, vaddr, size);
|
||||||
}
|
}
|
||||||
@@ -569,19 +527,14 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Buffer> BufferCache::ObtainNullBuffer() {
|
std::shared_ptr<Buffer> BufferCache::ObtainNullBuffer() {
|
||||||
std::shared_ptr<Buffer> buffer;
|
if (m_null_buffer != nullptr) {
|
||||||
{
|
return m_null_buffer;
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
if (m_null_buffer != nullptr) {
|
|
||||||
return m_null_buffer;
|
|
||||||
}
|
|
||||||
m_null_buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal,
|
|
||||||
0, AllFlags, 16);
|
|
||||||
buffer = m_null_buffer;
|
|
||||||
}
|
}
|
||||||
|
m_null_buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal, 0,
|
||||||
|
AllFlags, 16);
|
||||||
const std::array<uint8_t, 16> zeros {};
|
const std::array<uint8_t, 16> zeros {};
|
||||||
Upload(m_scheduler.Current(), *buffer, 0, zeros.data(), zeros.size());
|
Upload(m_scheduler.Current(), *m_null_buffer, 0, zeros.data(), zeros.size());
|
||||||
return buffer;
|
return m_null_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
||||||
@@ -599,11 +552,10 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
|
|||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
||||||
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
|
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
||||||
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
|
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||||
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
const bool has_dirty_buffer_source = !dirty.empty();
|
||||||
const bool has_dirty_buffer_source = !dirty.empty();
|
|
||||||
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size,
|
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size,
|
||||||
"image source");
|
"image source");
|
||||||
|
|
||||||
@@ -674,10 +626,9 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
|
|||||||
}
|
}
|
||||||
m_staging_buffer.Commit();
|
m_staging_buffer.Commit();
|
||||||
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
||||||
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
|
const bool has_dirty_buffer_source = !dirty.empty();
|
||||||
const bool has_dirty_buffer_source = !dirty.empty();
|
auto owner = find_owner();
|
||||||
auto owner = find_owner();
|
|
||||||
if (has_dirty_buffer_source && owner == m_buffers.end()) {
|
if (has_dirty_buffer_source && owner == m_buffers.end()) {
|
||||||
EXIT("BufferCache: GPU-dirty image source lost its native owner\n");
|
EXIT("BufferCache: GPU-dirty image source lost its native owner\n");
|
||||||
}
|
}
|
||||||
@@ -710,8 +661,7 @@ void BufferCache::WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data)
|
|||||||
}
|
}
|
||||||
Libs::LibKernel::Memory::WriteBacking(vaddr, data.data(), data.size());
|
Libs::LibKernel::Memory::WriteBacking(vaddr, data.data(), data.size());
|
||||||
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
const auto end = vaddr + data.size();
|
||||||
const auto end = vaddr + data.size();
|
|
||||||
for (auto& [address, cached]: m_buffers) {
|
for (auto& [address, cached]: m_buffers) {
|
||||||
const auto cached_end = address + cached->size;
|
const auto cached_end = address + cached->size;
|
||||||
const auto begin = std::max(vaddr, address);
|
const auto begin = std::max(vaddr, address);
|
||||||
@@ -836,7 +786,6 @@ bool BufferCache::IsRegionRegistered(uint64_t vaddr, uint64_t size) {
|
|||||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||||
EXIT("BufferCache: invalid registered-region query\n");
|
EXIT("BufferCache: invalid registered-region query\n");
|
||||||
}
|
}
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
// Cached buffers are ordered and non-overlapping. The last buffer beginning before the query
|
// Cached buffers are ordered and non-overlapping. The last buffer beginning before the query
|
||||||
// end is therefore the only possible intersection.
|
// end is therefore the only possible intersection.
|
||||||
const auto candidate = m_buffers.lower_bound(vaddr + size);
|
const auto candidate = m_buffers.lower_bound(vaddr + size);
|
||||||
@@ -852,7 +801,6 @@ bool BufferCache::IsRegionGpuModified(uint64_t vaddr, uint64_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCache::HasGpuDirtyBytes(uint64_t vaddr, uint64_t size) {
|
bool BufferCache::HasGpuDirtyBytes(uint64_t vaddr, uint64_t size) {
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
return !m_gpu_modified_ranges.Intersections(vaddr, size).empty();
|
return !m_gpu_modified_ranges.Intersections(vaddr, size).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,7 +824,6 @@ void BufferCache::RunGarbageCollector() {
|
|||||||
std::vector<RetiredBuffer> retires;
|
std::vector<RetiredBuffer> retires;
|
||||||
std::vector<std::pair<RetiredBuffer, std::vector<DownloadCopy>>> dirty_retires;
|
std::vector<std::pair<RetiredBuffer, std::vector<DownloadCopy>>> dirty_retires;
|
||||||
{
|
{
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
std::vector<uint64_t> candidates;
|
std::vector<uint64_t> candidates;
|
||||||
for (const auto& [address, owner]: m_buffers) {
|
for (const auto& [address, owner]: m_buffers) {
|
||||||
const auto& cached = *owner;
|
const auto& cached = *owner;
|
||||||
@@ -924,7 +871,6 @@ void BufferCache::RunGarbageCollector() {
|
|||||||
QueueGarbageDownload(copies, std::move(retire));
|
QueueGarbageDownload(copies, std::move(retire));
|
||||||
}
|
}
|
||||||
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
|
||||||
for (const auto& retire: retires) {
|
for (const auto& retire: retires) {
|
||||||
auto found = m_buffers.find(retire.address);
|
auto found = m_buffers.find(retire.address);
|
||||||
if (found == m_buffers.end() || found->second->size != retire.size ||
|
if (found == m_buffers.end() || found->second->size != retire.size ||
|
||||||
|
|||||||
+1
-2
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "common/abi.h"
|
#include "common/abi.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/threads.h"
|
|
||||||
#include "graphics/host_gpu/memoryTracker.h"
|
#include "graphics/host_gpu/memoryTracker.h"
|
||||||
#include "graphics/host_gpu/rangeSet.h"
|
#include "graphics/host_gpu/rangeSet.h"
|
||||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||||
@@ -60,6 +59,7 @@ public:
|
|||||||
void FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds = false);
|
void FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds = false);
|
||||||
void CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size, bool dst_gds = false,
|
void CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size, bool dst_gds = false,
|
||||||
bool src_gds = false);
|
bool src_gds = false);
|
||||||
|
// Cache-index and exact dirty-range queries require GPU-thread serialization.
|
||||||
[[nodiscard]] bool IsRegionRegistered(uint64_t vaddr, uint64_t size);
|
[[nodiscard]] bool IsRegionRegistered(uint64_t vaddr, uint64_t size);
|
||||||
[[nodiscard]] bool HasGpuDirtyBytes(uint64_t vaddr, uint64_t size);
|
[[nodiscard]] bool HasGpuDirtyBytes(uint64_t vaddr, uint64_t size);
|
||||||
[[nodiscard]] bool IsRegionCpuModified(uint64_t vaddr, uint64_t size);
|
[[nodiscard]] bool IsRegionCpuModified(uint64_t vaddr, uint64_t size);
|
||||||
@@ -99,7 +99,6 @@ private:
|
|||||||
GraphicContext& m_graphics;
|
GraphicContext& m_graphics;
|
||||||
CommandScheduler& m_scheduler;
|
CommandScheduler& m_scheduler;
|
||||||
Buffer m_gds_buffer;
|
Buffer m_gds_buffer;
|
||||||
Common::Mutex m_mutex;
|
|
||||||
std::shared_ptr<Buffer> m_null_buffer;
|
std::shared_ptr<Buffer> m_null_buffer;
|
||||||
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
||||||
RangeSet m_gpu_modified_ranges;
|
RangeSet m_gpu_modified_ranges;
|
||||||
|
|||||||
@@ -1642,6 +1642,7 @@ public:
|
|||||||
auto& gpu_scheduler = context.GetCommandScheduler();
|
auto& gpu_scheduler = context.GetCommandScheduler();
|
||||||
auto processor = std::make_unique<CommandProcessor>(context);
|
auto processor = std::make_unique<CommandProcessor>(context);
|
||||||
gpu.SendCommandSync([&] {
|
gpu.SendCommandSync([&] {
|
||||||
|
processor->BufferInit();
|
||||||
SubmitInfo blocked_submit;
|
SubmitInfo blocked_submit;
|
||||||
blocked_submit.AddWait(blocked_timeline, release_tick);
|
blocked_submit.AddWait(blocked_timeline, release_tick);
|
||||||
gpu_scheduler.Flush(blocked_submit);
|
gpu_scheduler.Flush(blocked_submit);
|
||||||
@@ -3228,40 +3229,17 @@ public:
|
|||||||
"prefix, or transferred ownership");
|
"prefix, or transferred ownership");
|
||||||
|
|
||||||
auto& buffer_cache = resources.GetBufferCache();
|
auto& buffer_cache = resources.GetBufferCache();
|
||||||
Require(name, "formatted lock-handoff fixture",
|
BufferBinding mip_formatted;
|
||||||
!buffer_cache.IsRegionRegistered(mip_desc.info.data.address,
|
bool formatted_owner_was_absent = false;
|
||||||
mip_desc.info.data.size),
|
gpu.SendCommandSync([&] {
|
||||||
"formatted image already had a cached Buffer owner");
|
formatted_owner_was_absent =
|
||||||
BufferBinding mip_formatted;
|
!buffer_cache.IsRegionRegistered(mip_desc.info.data.address,
|
||||||
std::binary_semaphore obtain_entered {0};
|
mip_desc.info.data.size);
|
||||||
std::binary_semaphore obtain_complete {0};
|
|
||||||
std::binary_semaphore buffer_visible {0};
|
|
||||||
auto texture_lock = TextureCacheTestAccess::Lock(texture_cache);
|
|
||||||
gpu.SendCommand([&] {
|
|
||||||
obtain_entered.release();
|
|
||||||
mip_formatted = buffer_cache.ObtainBuffer(command, mip_desc.info.data.address,
|
mip_formatted = buffer_cache.ObtainBuffer(command, mip_desc.info.data.address,
|
||||||
mip_desc.info.data.size, false, true, true);
|
mip_desc.info.data.size, false, true, true);
|
||||||
obtain_complete.release();
|
|
||||||
});
|
});
|
||||||
obtain_entered.acquire();
|
Require(name, "formatted cache fixture", formatted_owner_was_absent,
|
||||||
std::jthread buffer_probe([&] {
|
"formatted image already had a cached Buffer owner");
|
||||||
while (!buffer_cache.IsRegionRegistered(mip_desc.info.data.address,
|
|
||||||
mip_desc.info.data.size)) {
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
buffer_visible.release();
|
|
||||||
});
|
|
||||||
const bool buffer_released_before_texture =
|
|
||||||
buffer_visible.try_acquire_for(std::chrono::seconds(2));
|
|
||||||
texture_lock.unlock();
|
|
||||||
if (!buffer_released_before_texture) {
|
|
||||||
buffer_visible.acquire();
|
|
||||||
}
|
|
||||||
obtain_complete.acquire();
|
|
||||||
buffer_probe.join();
|
|
||||||
Require(name, "formatted cache lock handoff", buffer_released_before_texture,
|
|
||||||
"formatted Buffer acquisition retained the BufferCache lock while waiting for "
|
|
||||||
"TextureCache");
|
|
||||||
Require(name, "formatted Buffer path",
|
Require(name, "formatted Buffer path",
|
||||||
mip_formatted.owner != nullptr && mip_formatted.buffer != nullptr &&
|
mip_formatted.owner != nullptr && mip_formatted.buffer != nullptr &&
|
||||||
texture_cache.GetImage(mip_image).IsGpuModified(),
|
texture_cache.GetImage(mip_image).IsGpuModified(),
|
||||||
|
|||||||
Reference in New Issue
Block a user