diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 0bd4212288..d013941f6b 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1953,8 +1953,19 @@ void VKGSRender::prepare_rtts() (*m_device), &*m_current_command_buffer, m_optimal_tiling_supported_formats, m_memory_type_mapping); //Reset framebuffer information + VkFormat old_format = VK_FORMAT_UNDEFINED; for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i) { + //Flush old address if we keep missing it + if (m_surface_info[i].pitch && g_cfg.video.write_color_buffers) + { + if (old_format == VK_FORMAT_UNDEFINED) + old_format = vk::get_compatible_surface_format(m_surface_info[i].color_format).first; + + m_texture_cache.flush_if_cache_miss_likely(old_format, m_surface_info[i].address, m_surface_info[i].pitch * m_surface_info[i].height, + *m_current_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + } + m_surface_info[i].address = m_surface_info[i].pitch = 0; m_surface_info[i].width = clip_width; m_surface_info[i].height = clip_height; diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index bef9c189b5..ecde5ff526 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -118,6 +118,11 @@ namespace vk return managed_texture; } + VkFormat get_format() + { + return vram_texture->info.format; + } + bool is_flushable() const { //This section is active and can be flushed to cpu @@ -221,15 +226,19 @@ namespace vk } } - void flush(vk::render_device& dev, vk::command_buffer& cmd, u32 heap_index, VkQueue submit_queue) + bool flush(vk::render_device& dev, vk::command_buffer& cmd, u32 heap_index, VkQueue submit_queue) { if (m_device == nullptr) m_device = &dev; + // Return false if a flush occured 'late', i.e we had a miss + bool result = true; + if (!synchronized) { LOG_WARNING(RSX, "Cache miss at address 0x%X. This is gonna hurt...", cpu_address_base); copy_texture(cmd, heap_index, submit_queue, true); + result = false; } protect(utils::protection::rw); @@ -271,6 +280,8 @@ namespace vk dma_buffer->unmap(); //Its highly likely that this surface will be reused, so we just leave resources in place + + return result; } bool is_synchronized() const @@ -287,6 +298,16 @@ namespace vk std::vector > m_temporary_image_view; std::vector> m_dirty_textures; + // Keep track of cache misses to pre-emptively flush some addresses + struct framebuffer_memory_characteristics + { + u32 misses; + u32 block_size; + VkFormat format; + }; + + std::unordered_map m_cache_miss_statistics_table; + cached_texture_section& find_cached_texture(u32 rsx_address, u32 rsx_size, bool confirm_dimensions = false, u16 width = 0, u16 height = 0, u16 mipmaps = 0) { for (auto &tex : m_cache) @@ -676,7 +697,13 @@ namespace vk } //TODO: Map basic host_visible memory without coherent constraint - tex.flush(dev, cmd, memory_types.host_visible_coherent, submit_queue); + if (!tex.flush(dev, cmd, memory_types.host_visible_coherent, submit_queue)) + { + //Missed address, note this + //TODO: Lower severity when successful to keep the cache from overworking + record_cache_miss(tex); + } + response = true; } } @@ -727,5 +754,59 @@ namespace vk m_dirty_textures.clear(); m_temporary_image_view.clear(); } + + void record_cache_miss(cached_texture_section &tex) + { + const u32 memory_address = tex.get_section_base(); + const u32 memory_size = tex.get_section_size(); + const VkFormat fmt = tex.get_format(); + + auto It = m_cache_miss_statistics_table.find(memory_address); + if (It == m_cache_miss_statistics_table.end()) + { + m_cache_miss_statistics_table[memory_address] = { 1, memory_size, fmt }; + return; + } + + auto &value = It->second; + if (value.format != fmt || value.block_size != memory_size) + { + m_cache_miss_statistics_table[memory_address] = { 1, memory_size, fmt }; + return; + } + + value.misses++; + } + + void flush_if_cache_miss_likely(const VkFormat fmt, const u32 memory_address, const u32 memory_size, vk::command_buffer& cmd, vk::memory_type_mapping& memory_types, VkQueue submit_queue) + { + auto It = m_cache_miss_statistics_table.find(memory_address); + if (It == m_cache_miss_statistics_table.end()) + { + m_cache_miss_statistics_table[memory_address] = { 0, memory_size, fmt }; + return; + } + + auto value = It->second; + + if (value.format != fmt || value.block_size != memory_size) + { + //Reset since the data has changed + //TODO: Keep track of all this information together + m_cache_miss_statistics_table[memory_address] = { 0, memory_size, fmt }; + return; + } + + //Properly synchronized - no miss + if (!value.misses) return; + + //Auto flush if this address keeps missing (not properly synchronized) + if (value.misses > 16) + { + //TODO: Determine better way of getting threshold + LOG_ERROR(RSX, "Flushing memory at 0x%X -> Cache miss avoided", memory_address); + flush_memory_to_cache(memory_address, memory_size, cmd, memory_types, submit_queue); + } + } }; }