diff options
Diffstat (limited to 'src/video_core')
| -rw-r--r-- | src/video_core/nx2.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.cpp | 22 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 1 |
3 files changed, 24 insertions, 1 deletions
diff --git a/src/video_core/nx2.h b/src/video_core/nx2.h index 1a3bae93ec..4b0d3d5f62 100644 --- a/src/video_core/nx2.h +++ b/src/video_core/nx2.h @@ -96,7 +96,7 @@ inline void Nx2BackSparseWrite(u64 gpu_address, u64 size) { // Which blocks of each sparse region have been written (got memory), for the texture cache: an // image over blocks nobody wrote is all zeros and needs no device memory. Its own lock, taken last: -// the texture cache asks with its lock held, and nx2:gpu updates it holding none of the GPU's. +// the texture cache asks with its lock held, and nx2:gpu updates it holding only its region mutex. struct Nx2SparseRegionState { u64 size; u64 block_size; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index b77f9af5f8..9204ade9eb 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2620,6 +2620,20 @@ std::vector<u32> Image::BindLayers(std::span<const u32> layers) { const VkDeviceSize cap = SparseLayersCap(device); std::vector<VkSparseImageMemoryBind> image_binds; std::vector<VkSparseMemoryBind> opaque_binds; + const bool tail_was_bound = s.tail_memory != VK_NULL_HANDLE; + std::vector<u32> fresh; // Layers given memory by this call + // A failed bind leaves the layers as they were, so the next call tries them again. + const auto roll_back = [&] { + for (const u32 layer : fresh) { + s.allocator->FreeSparseMemory(s.layer_memory[layer]); + s.layer_memory[layer] = VK_NULL_HANDLE; + s.bound_bytes -= s.layer_bytes; + } + if (s.tail_memory != VK_NULL_HANDLE && !tail_was_bound) { + s.allocator->FreeSparseMemory(s.tail_memory); + s.tail_memory = VK_NULL_HANDLE; + } + }; for (const u32 layer : layers) { if (s.layer_memory[layer] != VK_NULL_HANDLE) { bound.push_back(layer); @@ -2647,6 +2661,7 @@ std::vector<u32> Image::BindLayers(std::span<const u32> layers) { } s.layer_memory[layer] = allocation; s.bound_bytes += s.layer_bytes; + fresh.push_back(layer); for (u32 level = 0; level < tail_lod; ++level) { const u32 width = (std::max)(s.width >> level, 1U); const u32 height = (std::max)(s.height >> level, 1U); @@ -2693,6 +2708,12 @@ std::vector<u32> Image::BindLayers(std::span<const u32> layers) { .memoryOffset = offset, .flags = 0, }); + } else { + LOG_WARNING(Render_Vulkan, "Sparse layered image {:#x}: out of device memory for the " + "mip tail", + gpu_addr); + roll_back(); + return {}; } } if (image_binds.empty() && opaque_binds.empty()) { @@ -2737,6 +2758,7 @@ std::vector<u32> Image::BindLayers(std::span<const u32> layers) { if (result != VK_SUCCESS) { LOG_ERROR(Render_Vulkan, "Sparse layered image {:#x}: binding failed ({})", gpu_addr, static_cast<int>(result)); + roll_back(); return {}; } fence.Wait(); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 3de82d2eb4..63abffed0f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -2999,6 +2999,7 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool if (True(image.flags & ImageFlagBits::SparseLayers)) { std::vector<u32> layers(static_cast<size_t>(image.info.resources.layers)); std::iota(layers.begin(), layers.end(), 0U); + // Layers over the image's memory cap stay unbacked (logged once). image.BindLayers(layers); } } |
