mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-06-10 03:35:00 -06:00
Merge 55cb746d44 into 101c6fedbc
This commit is contained in:
commit
8dd6b94cc3
@ -86,17 +86,20 @@ ImageId TextureCache::GetNullImage(const vk::Format format) {
|
||||
}
|
||||
|
||||
void TextureCache::ProcessDownloadImages() {
|
||||
std::scoped_lock lock{mutex};
|
||||
for (const ImageId image_id : download_images) {
|
||||
DownloadImageMemory(image_id);
|
||||
}
|
||||
download_images.clear();
|
||||
}
|
||||
|
||||
void TextureCache::DownloadImageMemory(ImageId image_id) {
|
||||
std::optional<TextureCache::PendingImageDownload> TextureCache::ScheduleImageDownload(
|
||||
ImageId image_id) {
|
||||
Image& image = slot_images[image_id];
|
||||
if (False(image.flags & ImageFlagBits::GpuModified)) {
|
||||
return;
|
||||
if (!image.SafeToDownload() || image.info.props.is_tiled || image.info.guest_address == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& download_buffer = buffer_cache.GetUtilityBuffer(MemoryUsage::Download);
|
||||
const u32 download_size = image.info.pitch * image.info.size.height *
|
||||
image.info.resources.layers * (image.info.num_bits / 8);
|
||||
@ -118,17 +121,66 @@ void TextureCache::DownloadImageMemory(ImageId image_id) {
|
||||
.imageOffset = {0, 0, 0},
|
||||
.imageExtent = {image.info.size.width, image.info.size.height, 1},
|
||||
};
|
||||
scheduler.EndRendering();
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {});
|
||||
cmdbuf.copyImageToBuffer(image.GetImage(), vk::ImageLayout::eTransferSrcOptimal,
|
||||
download_buffer.Handle(), image_download);
|
||||
image.Download(std::span{&image_download, 1}, download_buffer.Handle(), offset, download_size);
|
||||
|
||||
scheduler.DeferPriorityOperation(
|
||||
[this, device_addr = image.info.guest_address, download, download_size] {
|
||||
Core::Memory::Instance()->TryWriteBacking(std::bit_cast<u8*>(device_addr), download,
|
||||
download_size);
|
||||
});
|
||||
const u64 serial = ++image_download_serial;
|
||||
latest_image_downloads[image.info.guest_address] = serial;
|
||||
return PendingImageDownload{
|
||||
.serial = serial,
|
||||
.guest_address = image.info.guest_address,
|
||||
.data = download,
|
||||
.size = download_size,
|
||||
};
|
||||
}
|
||||
|
||||
void TextureCache::WritebackImageMemory(const PendingImageDownload& download) {
|
||||
const auto it = latest_image_downloads.find(download.guest_address);
|
||||
if (it == latest_image_downloads.end() || it->second != download.serial) {
|
||||
return;
|
||||
}
|
||||
if (!Core::Memory::Instance()->TryWriteBacking(std::bit_cast<u8*>(download.guest_address),
|
||||
download.data, download.size)) {
|
||||
return;
|
||||
}
|
||||
latest_image_downloads.erase(it);
|
||||
}
|
||||
|
||||
void TextureCache::DownloadImageMemory(ImageId image_id) {
|
||||
const auto download = ScheduleImageDownload(image_id);
|
||||
if (!download) {
|
||||
return;
|
||||
}
|
||||
scheduler.DeferPriorityOperation([this, download = *download] {
|
||||
std::scoped_lock lock{mutex};
|
||||
WritebackImageMemory(download);
|
||||
});
|
||||
}
|
||||
|
||||
void TextureCache::SynchronizeGuestMemory(VAddr address, size_t size, const Image* excluded_image) {
|
||||
if (!readback_linear_images) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::container::small_vector<PendingImageDownload, 8> downloads;
|
||||
ForEachImageInRegion(address, size, [&](ImageId image_id, Image& image) {
|
||||
if (&image == excluded_image) {
|
||||
return;
|
||||
}
|
||||
if (auto download = ScheduleImageDownload(image_id)) {
|
||||
downloads.push_back(*download);
|
||||
download_images.erase(image_id);
|
||||
}
|
||||
});
|
||||
if (downloads.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The requested alias may be uploaded from guest memory immediately after this call. Wait for
|
||||
// overlapping linear images to reach the download buffer before exposing their contents.
|
||||
scheduler.Finish();
|
||||
for (const auto& download : downloads) {
|
||||
WritebackImageMemory(download);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) {
|
||||
@ -557,6 +609,13 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_fmt) {
|
||||
image_id = cache_id;
|
||||
}
|
||||
|
||||
// A different representation may be uploaded from guest memory while an overlapping linear
|
||||
// image still contains newer GPU-written data. Resolve pending writes before touching the
|
||||
// overlap set.
|
||||
if (!image_id) {
|
||||
SynchronizeGuestMemory(info.guest_address, info.guest_size);
|
||||
}
|
||||
|
||||
// Try to resolve overlaps (if any)
|
||||
int view_mip{-1};
|
||||
int view_slice{-1};
|
||||
@ -721,6 +780,10 @@ void TextureCache::RefreshImage(Image& image) {
|
||||
return;
|
||||
}
|
||||
|
||||
// RefreshImage reuploads from guest memory. Make overlapping GPU-written linear aliases
|
||||
// visible before obtaining the upload source.
|
||||
SynchronizeGuestMemory(image.info.guest_address, image.info.guest_size, &image);
|
||||
|
||||
RENDERER_TRACE;
|
||||
TRACE_HINT(fmt::format("{:x}:{:x}", image.info.guest_address, image.info.guest_size));
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
@ -271,9 +272,25 @@ private:
|
||||
/// Gets or creates a null image for a particular format.
|
||||
ImageId GetNullImage(vk::Format format);
|
||||
|
||||
/// Copies image memory back to CPU.
|
||||
struct PendingImageDownload {
|
||||
u64 serial;
|
||||
VAddr guest_address;
|
||||
u8* data;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
/// Schedules a copy of linear image memory into the download buffer.
|
||||
[[nodiscard]] std::optional<PendingImageDownload> ScheduleImageDownload(ImageId image_id);
|
||||
|
||||
/// Copies a completed image download into guest memory unless a newer download superseded it.
|
||||
void WritebackImageMemory(const PendingImageDownload& download);
|
||||
|
||||
/// Copies image memory back to CPU asynchronously.
|
||||
void DownloadImageMemory(ImageId image_id);
|
||||
|
||||
/// Makes GPU-written linear aliases visible before reuploading overlapping guest memory.
|
||||
void SynchronizeGuestMemory(VAddr address, size_t size, const Image* excluded_image = nullptr);
|
||||
|
||||
/// Thread function for copying downloaded images out to CPU memory.
|
||||
void DownloadedImagesThread(const std::stop_token& token);
|
||||
|
||||
@ -323,6 +340,8 @@ private:
|
||||
tsl::robin_map<u64, Sampler> samplers;
|
||||
tsl::robin_map<vk::Format, ImageId> null_images;
|
||||
std::unordered_set<ImageId> download_images;
|
||||
tsl::robin_map<VAddr, u64> latest_image_downloads;
|
||||
u64 image_download_serial{};
|
||||
u64 total_used_memory = 0;
|
||||
u64 trigger_gc_memory = 0;
|
||||
u64 pressure_gc_memory = 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user