From 3f1be5a4cea1c8b387965b0e86c10df9fbfa584d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Tue, 26 Nov 2024 07:48:14 +0100 Subject: [PATCH 1/2] Adding Utils Icon (#1600) --- REUSE.toml | 1 + src/images/utils_icon.png | Bin 0 -> 1470 bytes src/qt_gui/main_window.cpp | 1 + src/qt_gui/main_window_ui.h | 1 + src/shadps4.qrc | 1 + 5 files changed, 4 insertions(+) create mode 100644 src/images/utils_icon.png diff --git a/REUSE.toml b/REUSE.toml index 7b2862e53..2d94c9292 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -37,6 +37,7 @@ path = [ "src/images/refresh_icon.png", "src/images/settings_icon.png", "src/images/stop_icon.png", + "src/images/utils_icon.png", "src/images/shadPS4.icns", "src/images/shadps4.ico", "src/images/net.shadps4.shadPS4.svg", diff --git a/src/images/utils_icon.png b/src/images/utils_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7dfa3aa00f4de0d598c195719d8701c1357eb693 GIT binary patch literal 1470 zcmb`H{Xf$Q0LQ-!vF0f$^LQI6(Js~|k7bO_Lz?Hw%EN|hkxh11u9IpgxnVL7-Cd^} zUP%wgQ&@x%%ENhnI`bIjA=V_E{)ccHdIJ+2#l@RL%H?|R+&`P!s39F<;!(3&iU{j0~-{w zIc%KQIBOa9yj(mM)<^uj`<4|nQykMbR!^+B`m9vxtUT-v`*@ygpn7} zEjl{@C5GxMC_b?ay3pkOi-bc+yRlI}eoq4B+xUapDxcklY}zPrOqbZJ*ZFL3Rs)CN ziKI|j?MBVgYko^?)1q`-;J(N$2y_ymED$Ose#B=wNOOf6Zsj1#$RL)5gWs}VoI_%A zifXC_DtH4nHg&M#+C<&R==V+a3LO$bq0ctv*uM8eYf0Zt>;PplS;|z@`EUyE!A}Xn ze0I!P`t1-o5prHDM!@yJyM43}W*wuwUOcJn-2kJc#H7K}bNH-Fbq>w0^8{K{9_ZzIs=MzCvo7g6(un#XdD z5A`wegQY@HbVfxiPm5;#rwi4j{}Uo@rgK4!Ech~GNTIc6DP%BQ+$i*>h9|r?5jQLL zLDzDt@Yxv>v&-E(kGEaa175xe-X<4q)~<&PcA?EJ4zk7M(MdI}t+jc+RY=h1_VFD@ zR++IpEk^W5|G=$b{#3x{FZoe|kCM|&iIY`6}SwmlLe_bMkIW#i0UzJ%IDq^gc zV|N>GG&Qtg#2>Qz->2I#0;rMnFtV-kL!42Gf@0~rJBv%UlP2bailEcM35;1^0kyZQ zfJ5@s9mXHlO7`ePvW?QQPnpTZ2-Ga~Po0d15lYgWPQoBo> zcxA2`-qwZ2nz7G2{q>hkMcI+HM±H+D?ODdykm8mk4zHxPT*3zU${Z`8B-+#s))BnW}c_6{;|HX)Qm{-l*Ctv zRl0@Tqq#+3>#O8AuOrB|!c-fU=qs4{GPIZP?uc8gI>#ZXiE%YtcaDeSgGDq30d{qV zqDudfXIlSof^v$_RfUi1Mx{DgwuL8o0*mGzb%?ZAY5%fAiL+d7XE!EhivzPsoip4X z(%tfJ=4nop3qlMf7Aq`vM%0)*XLW!4)12cfoAsMR%QR7 sVoT_>eV`>wDxj}`u7Z(=$wcnRq7ID_8gameInstallPathAct->setIcon(RecolorIcon(ui->gameInstallPathAct->icon(), isWhite)); ui->menuThemes->setIcon(RecolorIcon(ui->menuThemes->icon(), isWhite)); ui->menuGame_List_Icons->setIcon(RecolorIcon(ui->menuGame_List_Icons->icon(), isWhite)); + ui->menuUtils->setIcon(RecolorIcon(ui->menuUtils->icon(), isWhite)); ui->playButton->setIcon(RecolorIcon(ui->playButton->icon(), isWhite)); ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite)); ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite)); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index cb9aa5904..5ff572f86 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -249,6 +249,7 @@ public: menuSettings->setObjectName("menuSettings"); menuUtils = new QMenu(menuSettings); menuUtils->setObjectName("menuUtils"); + menuUtils->setIcon(QIcon(":images/utils_icon.png")); menuThemes = new QMenu(menuView); menuThemes->setObjectName("menuThemes"); menuThemes->setIcon(QIcon(":images/themes_icon.png")); diff --git a/src/shadps4.qrc b/src/shadps4.qrc index a59cb0621..e328f2c42 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -6,6 +6,7 @@ images/play_icon.png images/pause_icon.png images/stop_icon.png + images/utils_icon.png images/file_icon.png images/folder_icon.png images/themes_icon.png From 18a36c5daa1fd2ff298f555b82936903e0e44a71 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 26 Nov 2024 23:45:15 +0300 Subject: [PATCH 2/2] Fixed false-positive image reuploads (#1557) * Fixed false-positive image reuploads * Fixed userfaultfd path, removed dead code, simplified calculations * oopsie * track potentially dirty images and hash them * untrack only first page of the image in case of head access * rebase, initialize hash, fix bounds check * include image tail in the calculations --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- src/video_core/page_manager.cpp | 16 +- src/video_core/page_manager.h | 3 + .../renderer_vulkan/vk_rasterizer.cpp | 6 +- .../renderer_vulkan/vk_rasterizer.h | 2 +- src/video_core/texture_cache/image.cpp | 3 +- src/video_core/texture_cache/image.h | 20 +- .../texture_cache/texture_cache.cpp | 176 +++++++++++++++--- src/video_core/texture_cache/texture_cache.h | 8 +- 9 files changed, 196 insertions(+), 40 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 92c446fa9..77b353c2f 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -635,7 +635,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, "Texel buffer aliases image subresources {:x} : {:x}", device_addr, image.info.guest_address); boost::container::small_vector copies; - u32 offset = buffer.Offset(image.cpu_addr); + u32 offset = buffer.Offset(image.info.guest_address); const u32 num_layers = image.info.resources.layers; const u32 max_offset = offset + size; for (u32 m = 0; m < image.info.resources.levels; m++) { diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 8c20ee6ed..d26a7067a 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -114,8 +114,8 @@ struct PageManager::Impl { // Notify rasterizer about the fault. const VAddr addr = msg.arg.pagefault.address; - const VAddr addr_page = Common::AlignDown(addr, PAGESIZE); - rasterizer->InvalidateMemory(addr_page, PAGESIZE); + const VAddr addr_page = GetPageAddr(addr); + rasterizer->InvalidateMemory(addr, addr_page, PAGESIZE); } } @@ -157,8 +157,8 @@ struct PageManager::Impl { const auto addr = reinterpret_cast(fault_address); const bool is_write = Common::IsWriteError(context); if (is_write && owned_ranges.find(addr) != owned_ranges.end()) { - const VAddr addr_aligned = Common::AlignDown(addr, PAGESIZE); - rasterizer->InvalidateMemory(addr_aligned, PAGESIZE); + const VAddr addr_aligned = GetPageAddr(addr); + rasterizer->InvalidateMemory(addr, addr_aligned, PAGESIZE); return true; } return false; @@ -174,6 +174,14 @@ PageManager::PageManager(Vulkan::Rasterizer* rasterizer_) PageManager::~PageManager() = default; +VAddr PageManager::GetPageAddr(VAddr addr) { + return Common::AlignDown(addr, PAGESIZE); +} + +VAddr PageManager::GetNextPageAddr(VAddr addr) { + return Common::AlignUp(addr + 1, PAGESIZE); +} + void PageManager::OnGpuMap(VAddr address, size_t size) { impl->OnMap(address, size); } diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index 0dc022aa5..29a946a8f 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -28,6 +28,9 @@ public: /// Increase/decrease the number of surface in pages touching the specified region void UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta); + static VAddr GetPageAddr(VAddr addr); + static VAddr GetNextPageAddr(VAddr addr); + private: struct Impl; std::unique_ptr impl; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 8dc0771de..e66d12517 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -792,9 +792,9 @@ u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { return value; } -void Rasterizer::InvalidateMemory(VAddr addr, u64 size) { - buffer_cache.InvalidateMemory(addr, size); - texture_cache.InvalidateMemory(addr, size); +void Rasterizer::InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size) { + buffer_cache.InvalidateMemory(addr_aligned, size); + texture_cache.InvalidateMemory(addr, addr_aligned, size); } void Rasterizer::MapMemory(VAddr addr, u64 size) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 5102cda38..fe8aceba7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -46,7 +46,7 @@ public: void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); - void InvalidateMemory(VAddr addr, u64 size); + void InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size); void MapMemory(VAddr addr, u64 size); void UnmapMemory(VAddr addr, u64 size); diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index dc43036c6..3d5202ad6 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -144,8 +144,7 @@ void UniqueImage::Create(const vk::ImageCreateInfo& image_ci) { Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, const ImageInfo& info_) : instance{&instance_}, scheduler{&scheduler_}, info{info_}, - image{instance->GetDevice(), instance->GetAllocator()}, cpu_addr{info.guest_address}, - cpu_addr_end{cpu_addr + info.guest_size_bytes} { + image{instance->GetDevice(), instance->GetAllocator()} { mip_hashes.resize(info.resources.levels); ASSERT(info.pixel_format != vk::Format::eUndefined); // Here we force `eExtendedUsage` as don't know all image usage cases beforehand. In normal case diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 8d84277d8..a1b1b007f 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -22,11 +22,12 @@ VK_DEFINE_HANDLE(VmaAllocator) namespace VideoCore { enum ImageFlagBits : u32 { - CpuDirty = 1 << 1, ///< Contents have been modified from the CPU + Empty = 0, + MaybeCpuDirty = 1 << 0, ///< The page this image is in was touched before the image address + CpuDirty = 1 << 1, ///< Contents have been modified from the CPU GpuDirty = 1 << 2, ///< Contents have been modified from the GPU (valid data in buffer cache) - Dirty = CpuDirty | GpuDirty, + Dirty = MaybeCpuDirty | CpuDirty | GpuDirty, GpuModified = 1 << 3, ///< Contents have been modified from the GPU - Tracked = 1 << 4, ///< Writes and reads are being hooked from the CPU Registered = 1 << 6, ///< True when the image is registered Picked = 1 << 7, ///< Temporary flag to mark the image as picked MetaRegistered = 1 << 8, ///< True when metadata for this surface is known and registered @@ -78,7 +79,9 @@ struct Image { [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { const VAddr overlap_end = overlap_cpu_addr + overlap_size; - return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; + const auto image_addr = info.guest_address; + const auto image_end = info.guest_address + info.guest_size_bytes; + return image_addr < overlap_end && overlap_cpu_addr < image_end; } ImageViewId FindView(const ImageViewInfo& info) const { @@ -99,14 +102,18 @@ struct Image { void CopyImage(const Image& image); void CopyMip(const Image& image, u32 mip); + bool IsTracked() { + return track_addr != 0 && track_addr_end != 0; + } + const Vulkan::Instance* instance; Vulkan::Scheduler* scheduler; ImageInfo info; UniqueImage image; vk::ImageAspectFlags aspect_mask = vk::ImageAspectFlagBits::eColor; ImageFlagBits flags = ImageFlagBits::Dirty; - VAddr cpu_addr = 0; - VAddr cpu_addr_end = 0; + VAddr track_addr = 0; + VAddr track_addr_end = 0; std::vector image_view_infos; std::vector image_view_ids; @@ -130,6 +137,7 @@ struct Image { std::vector subresource_states{}; boost::container::small_vector mip_hashes{}; u64 tick_accessed_last{0}; + u64 hash{0}; struct { union { diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 516c110a4..4373fdc52 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -29,9 +29,12 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& info.UpdateSize(); const ImageId null_id = slot_images.insert(instance, scheduler, info); ASSERT(null_id.index == NULL_IMAGE_ID.index); - const vk::Image& null_image = slot_images[null_id].image; + auto& img = slot_images[null_id]; + const vk::Image& null_image = img.image; Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image"); - slot_images[null_id].flags = ImageFlagBits::Tracked; + img.flags = ImageFlagBits::Empty; + img.track_addr = img.info.guest_address; + img.track_addr_end = img.info.guest_address + img.info.guest_size_bytes; ImageViewInfo view_info; const auto null_view_id = @@ -43,13 +46,43 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& TextureCache::~TextureCache() = default; -void TextureCache::InvalidateMemory(VAddr address, size_t size) { +void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { + if (image.hash == 0) { + // Initialize hash + const u8* addr = std::bit_cast(image.info.guest_address); + image.hash = XXH3_64bits(addr, image.info.guest_size_bytes); + } + image.flags |= ImageFlagBits::MaybeCpuDirty; + UntrackImage(image_id); +} + +void TextureCache::InvalidateMemory(VAddr addr, VAddr page_addr, size_t size) { std::scoped_lock lock{mutex}; - ForEachImageInRegion(address, size, [&](ImageId image_id, Image& image) { - // Ensure image is reuploaded when accessed again. - image.flags |= ImageFlagBits::CpuDirty; - // Untrack image, so the range is unprotected and the guest can write freely. - UntrackImage(image_id); + ForEachImageInRegion(page_addr, size, [&](ImageId image_id, Image& image) { + const auto image_begin = image.info.guest_address; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto page_end = page_addr + size; + if (image_begin <= addr && addr < image_end) { + // This image was definitely accessed by this page fault. + // Untrack image, so the range is unprotected and the guest can write freely + image.flags |= ImageFlagBits::CpuDirty; + UntrackImage(image_id); + } else if (page_end < image_end) { + // This page access may or may not modify the image. + // We should not mark it as dirty now. If it really was modified + // it will receive more invalidations on its other pages. + // Remove tracking from this page only. + UntrackImageHead(image_id); + } else if (image_begin < page_addr) { + // This page access does not modify the image but the page should be untracked. + // We should not mark this image as dirty now. If it really was modified + // it will receive more invalidations on its other pages. + UntrackImageTail(image_id); + } else { + // Image begins and ends on this page so it can not receive any more invalidations. + // We will check it's hash later to see if it really was modified. + MarkAsMaybeDirty(image_id, image); + } }); } @@ -415,6 +448,23 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule return; } + if (True(image.flags & ImageFlagBits::MaybeCpuDirty) && + False(image.flags & ImageFlagBits::CpuDirty)) { + // The image size should be less than page size to be considered MaybeCpuDirty + // So this calculation should be very uncommon and reasonably fast + // For now we'll just check up to 64 first pixels + const auto addr = std::bit_cast(image.info.guest_address); + const auto w = std::min(image.info.size.width, u32(8)); + const auto h = std::min(image.info.size.height, u32(8)); + const auto size = w * h * image.info.num_bits / 8; + const u64 hash = XXH3_64bits(addr, size); + if (image.hash == hash) { + image.flags &= ~ImageFlagBits::MaybeCpuDirty; + return; + } + image.hash = hash; + } + const auto& num_layers = image.info.resources.layers; const auto& num_mips = image.info.resources.levels; ASSERT(num_mips == image.info.mips_layout.size()); @@ -425,14 +475,14 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const u32 height = std::max(image.info.size.height >> m, 1u); const u32 depth = image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; - const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; + const auto& mip = image.info.mips_layout[m]; // Protect GPU modified resources from accidental CPU reuploads. const bool is_gpu_modified = True(image.flags & ImageFlagBits::GpuModified); const bool is_gpu_dirty = True(image.flags & ImageFlagBits::GpuDirty); if (is_gpu_modified && !is_gpu_dirty) { const u8* addr = std::bit_cast(image.info.guest_address); - const u64 hash = XXH3_64bits(addr + mip_ofs, mip_size); + const u64 hash = XXH3_64bits(addr + mip.offset, mip.size); if (image.mip_hashes[m] == hash) { continue; } @@ -440,9 +490,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule } image_copy.push_back({ - .bufferOffset = mip_ofs * num_layers, - .bufferRowLength = static_cast(mip_pitch), - .bufferImageHeight = static_cast(mip_height), + .bufferOffset = mip.offset * num_layers, + .bufferRowLength = static_cast(mip.pitch), + .bufferImageHeight = static_cast(mip.height), .imageSubresource{ .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, @@ -455,6 +505,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule } if (image_copy.empty()) { + image.flags &= ~ImageFlagBits::Dirty; return; } @@ -500,7 +551,7 @@ void TextureCache::RegisterImage(ImageId image_id) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Trying to register an already registered image"); image.flags |= ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.info.guest_size_bytes, + ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { page_table[page].push_back(image_id); }); } @@ -509,7 +560,7 @@ void TextureCache::UnregisterImage(ImageId image_id) { ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), "Trying to unregister an already unregistered image"); image.flags &= ~ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.info.guest_size_bytes, [this, image_id](u64 page) { + ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { const auto page_it = page_table.find(page); if (page_it == nullptr) { UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PageShift); @@ -527,25 +578,106 @@ void TextureCache::UnregisterImage(ImageId image_id) { void TextureCache::TrackImage(ImageId image_id) { auto& image = slot_images[image_id]; - if (True(image.flags & ImageFlagBits::Tracked)) { + const auto image_begin = image.info.guest_address; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (image_begin == image.track_addr && image_end == image.track_addr_end) { return; } - image.flags |= ImageFlagBits::Tracked; - tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, 1); + + if (!image.IsTracked()) { + // Re-track the whole image + image.track_addr = image_begin; + image.track_addr_end = image_end; + tracker.UpdatePagesCachedCount(image_begin, image.info.guest_size_bytes, 1); + } else { + if (image_begin < image.track_addr) { + TrackImageHead(image_id); + } + if (image.track_addr_end < image_end) { + TrackImageTail(image_id); + } + } +} + +void TextureCache::TrackImageHead(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_begin = image.info.guest_address; + if (image_begin == image.track_addr) { + return; + } + ASSERT(image.track_addr != 0 && image_begin < image.track_addr); + const auto size = image.track_addr - image_begin; + image.track_addr = image_begin; + tracker.UpdatePagesCachedCount(image_begin, size, 1); +} + +void TextureCache::TrackImageTail(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (image_end == image.track_addr_end) { + return; + } + ASSERT(image.track_addr_end != 0 && image.track_addr_end < image_end); + const auto addr = image.track_addr_end; + const auto size = image_end - image.track_addr_end; + image.track_addr_end = image_end; + tracker.UpdatePagesCachedCount(addr, size, 1); } void TextureCache::UntrackImage(ImageId image_id) { auto& image = slot_images[image_id]; - if (False(image.flags & ImageFlagBits::Tracked)) { + if (!image.IsTracked()) { return; } - image.flags &= ~ImageFlagBits::Tracked; - tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, -1); + const auto addr = image.track_addr; + const auto size = image.track_addr_end - image.track_addr; + image.track_addr = 0; + image.track_addr_end = 0; + if (size != 0) { + tracker.UpdatePagesCachedCount(addr, size, -1); + } +} + +void TextureCache::UntrackImageHead(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_begin = image.info.guest_address; + if (!image.IsTracked() || image_begin < image.track_addr) { + return; + } + const auto addr = tracker.GetNextPageAddr(image_begin); + const auto size = addr - image_begin; + image.track_addr = addr; + if (image.track_addr == image.track_addr_end) { + // This image spans only 2 pages and both are modified, + // but the image itself was not directly affected. + // Cehck its hash later. + MarkAsMaybeDirty(image_id, image); + } + tracker.UpdatePagesCachedCount(image_begin, size, -1); +} + +void TextureCache::UntrackImageTail(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (!image.IsTracked() || image.track_addr_end < image_end) { + return; + } + ASSERT(image.track_addr_end != 0); + const auto addr = tracker.GetPageAddr(image_end); + const auto size = image_end - addr; + image.track_addr_end = addr; + if (image.track_addr == image.track_addr_end) { + // This image spans only 2 pages and both are modified, + // but the image itself was not directly affected. + // Cehck its hash later. + MarkAsMaybeDirty(image_id, image); + } + tracker.UpdatePagesCachedCount(addr, size, -1); } void TextureCache::DeleteImage(ImageId image_id) { Image& image = slot_images[image_id]; - ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked"); + ASSERT_MSG(!image.IsTracked(), "Image was not untracked"); ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); // Remove any registered meta areas. diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8ac603f06..fab4c832f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -95,7 +95,7 @@ public: ~TextureCache(); /// Invalidates any image in the logical page range. - void InvalidateMemory(VAddr address, size_t size); + void InvalidateMemory(VAddr addr, VAddr page_addr, size_t size); /// Marks an image as dirty if it exists at the provided address. void InvalidateMemoryFromGPU(VAddr address, size_t max_size); @@ -242,9 +242,15 @@ private: /// Track CPU reads and writes for image void TrackImage(ImageId image_id); + void TrackImageHead(ImageId image_id); + void TrackImageTail(ImageId image_id); /// Stop tracking CPU reads and writes for image void UntrackImage(ImageId image_id); + void UntrackImageHead(ImageId image_id); + void UntrackImageTail(ImageId image_id); + + void MarkAsMaybeDirty(ImageId image_id, Image& image); /// Removes the image and any views/surface metas that reference it. void DeleteImage(ImageId image_id);