diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 11de37eb6b..de15e13da3 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1499,9 +1499,17 @@ void TextureCache

::TickAsyncDecode() { auto i = async_decodes.begin(); while (i != async_decodes.end()) { auto* async_decode = i->get(); - std::unique_lock lock{async_decode->mutex}; - if (!async_decode->complete) { - ++i; + { + // Release the lock before the context is erased below: unlocking it afterwards wrote + // into freed memory (heap corruption, crashes minutes into title 1). + std::unique_lock lock{async_decode->mutex}; + if (!async_decode->complete) { + ++i; + continue; + } + } + if (async_decode->cancelled) { + i = async_decodes.erase(i); continue; } Image& image = slot_images[async_decode->image_id]; @@ -2452,6 +2460,14 @@ void TextureCache

::UntrackImage(ImageBase& image, ImageId image_id) { template void TextureCache

::DeleteImage(ImageId image_id, bool immediate_delete) { ImageBase& image = slot_images[image_id]; + if (True(image.flags & ImageFlagBits::IsDecoding)) { + // A worker is still decoding it: keep its context alive but drop the result. + for (auto& async_decode : async_decodes) { + if (async_decode->image_id == image_id) { + async_decode->cancelled = true; + } + } + } if (image.HasScaled()) { total_used_memory -= GetScaledImageSizeBytes(image); } diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index d103d66189..ed9758fdee 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -68,6 +68,7 @@ struct AsyncDecodeContext { boost::container::small_vector copies; std::mutex mutex; std::atomic_bool complete; + bool cancelled{}; ///< The image was deleted meanwhile: drop the result (GPU thread only). }; using TextureCacheGPUMap = ::Common::unordered_map, Common::IdentityHash>;