diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index 6964dfadad..8610032b63 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -69,6 +69,8 @@ namespace rsx rsx::texture_dimension_extended image_type = texture_dimension_extended::texture_dimension_2d; rsx::format_class format_class = RSX_FORMAT_CLASS_UNDEFINED; bool is_cyclic_reference = false; + u32 ref_address = 0; + u64 surface_cache_tag = 0; f32 scale_x = 1.f; f32 scale_y = 1.f; diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index d2258c6a7f..1e8fb6296a 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -796,8 +796,6 @@ namespace rsx { invalidate(It->second); m_render_targets_storage.erase(It); - - cache_tag = rsx::get_shared_tag(); return; } } @@ -808,8 +806,6 @@ namespace rsx { invalidate(It->second); m_depth_stencil_storage.erase(It); - - cache_tag = rsx::get_shared_tag(); return; } } @@ -1033,11 +1029,6 @@ namespace rsx } } - void notify_memory_structure_changed() - { - cache_tag = rsx::get_shared_tag(); - } - void invalidate_all() { // Unbind and invalidate all resources diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 35cffdc658..7adf15315c 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -263,6 +263,37 @@ namespace rsx { return (image_handle || external_subresource_desc.op != deferred_request_command::nop); } + + /** + * Returns a boolean true/false if the descriptor is expired + * Optionally returns a second variable that contains the surface reference. + * The surface reference can be used to insert a texture barrier or inject a deferred resource + */ + template + std::pair is_expired(surface_store_type& surface_cache) + { + if (upload_context != rsx::texture_upload_context::framebuffer_storage || + surface_cache_tag == surface_cache.cache_tag) + { + return { false, nullptr }; + } + + // Expired, but may still be valid. Check if the texture is still accessible + auto ref_image = image_handle ? image_handle->image() : external_subresource_desc.external_handle; + if (ref_image) + { + if (auto as_rtt = dynamic_cast(ref_image); + as_rtt && as_rtt == surface_cache.get_surface_at(ref_address)) + { + // Fast sync + surface_cache_tag = surface_cache.cache_tag; + is_cyclic_reference = surface_cache.address_is_bound(ref_address); + return { false, as_rtt }; + } + } + + return { true, nullptr }; + } }; @@ -322,7 +353,7 @@ namespace rsx const std::vector& subresource_layout, rsx::texture_dimension_extended type, bool swizzled) = 0; virtual section_storage_type* create_nul_section(commandbuffer_type&, const address_range &rsx_range, bool memory_load) = 0; virtual void enforce_surface_creation_type(section_storage_type& section, u32 gcm_format, texture_create_flags expected) = 0; - virtual void insert_texture_barrier(commandbuffer_type&, image_storage_type* tex) = 0; + virtual void insert_texture_barrier(commandbuffer_type&, image_storage_type* tex, bool strong_ordering = true) = 0; virtual image_view_type generate_cubemap_from_images(commandbuffer_type&, u32 gcm_format, u16 size, const std::vector& sources, const texture_channel_remap_t& remap_vector) = 0; virtual image_view_type generate_3d_from_2d_images(commandbuffer_type&, u32 gcm_format, u16 width, u16 height, u16 depth, const std::vector& sources, const texture_channel_remap_t& remap_vector) = 0; virtual image_view_type generate_atlas_from_images(commandbuffer_type&, u32 gcm_format, u16 width, u16 height, const std::vector& sections_to_copy, const texture_channel_remap_t& remap_vector) = 0; @@ -1743,6 +1774,39 @@ namespace rsx return {}; } + template + bool test_if_descriptor_expired(commandbuffer_type& cmd, surface_store_type& surface_cache, sampled_image_descriptor* descriptor) + { + auto result = descriptor->is_expired(surface_cache); + if (result.second && descriptor->is_cyclic_reference) + { + /* NOTE: All cyclic descriptors updated via fast update must have a barrier check + * It is possible for the following sequence of events to break common-sense tests + * 1. Cyclic ref occurs normally in upload_texture + * 2. Surface is swappd out, but texture is not updated + * 3. Surface is swapped back in. Surface cache resets layout to optimal rasterization layout + * 4. During bind, the surface is converted to shader layout because it is not in GENERAL layout + */ + if (!g_cfg.video.strict_rendering_mode) + { + insert_texture_barrier(cmd, result.second, false); + } + else if (descriptor->image_handle) + { + descriptor->external_subresource_desc.external_handle = descriptor->image_handle->image(); + descriptor->external_subresource_desc.op = deferred_request_command::copy_image_dynamic; + descriptor->external_subresource_desc.do_not_cache = true; + } + else + { + // Force reupload + return true; + } + } + + return result.first; + } + template sampled_image_descriptor upload_texture(commandbuffer_type& cmd, RsxTextureType& tex, surface_store_type& m_rtts, Args&&... extras) { @@ -1847,6 +1911,9 @@ namespace rsx result.external_subresource_desc.cache_range = lookup_range; } + result.ref_address = attributes.address; + result.surface_cache_tag = m_rtts.cache_tag; + if (subsurface_count == 1) { return result; @@ -2727,7 +2794,6 @@ namespace rsx // NOTE: This doesn't work very well in case of Cell access // Need to lock the affected memory range and actually attach this subres to a locked_region dst_subres.surface->on_write_copy(rsx::get_shared_tag(), false, raster_type); - m_rtts.notify_memory_structure_changed(); // Reset this object's synchronization status if it is locked lock.upgrade(); diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 1e74ab32fd..75b9c5553b 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -274,16 +274,8 @@ void GLGSRender::load_texture_env() { // Load textures gl::command_context cmd{ gl_state }; - bool update_framebuffer_sourced = false; - std::lock_guard lock(m_sampler_mutex); - if (surface_store_tag != m_rtts.cache_tag) - { - update_framebuffer_sourced = true; - surface_store_tag = m_rtts.cache_tag; - } - for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i) { if (!(textures_ref & 1)) @@ -292,11 +284,9 @@ void GLGSRender::load_texture_env() if (!fs_sampler_state[i]) fs_sampler_state[i] = std::make_unique(); - if (m_samplers_dirty || m_textures_dirty[i] || - (update_framebuffer_sourced && fs_sampler_state[i]->upload_context == rsx::texture_upload_context::framebuffer_storage)) + auto sampler_state = static_cast(fs_sampler_state[i].get()); + if (m_samplers_dirty || m_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state)) { - auto sampler_state = static_cast(fs_sampler_state[i].get()); - if (rsx::method_registers.fragment_textures[i].enabled()) { *sampler_state = m_gl_texture_cache.upload_texture(cmd, rsx::method_registers.fragment_textures[i], m_rtts); @@ -321,11 +311,9 @@ void GLGSRender::load_texture_env() if (!vs_sampler_state[i]) vs_sampler_state[i] = std::make_unique(); - if (m_samplers_dirty || m_vertex_textures_dirty[i] || - (update_framebuffer_sourced && vs_sampler_state[i]->upload_context == rsx::texture_upload_context::framebuffer_storage)) + auto sampler_state = static_cast(vs_sampler_state[i].get()); + if (m_samplers_dirty || m_vertex_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state)) { - auto sampler_state = static_cast(vs_sampler_state[i].get()); - if (rsx::method_registers.vertex_textures[i].enabled()) { *sampler_state = m_gl_texture_cache.upload_texture(cmd, rsx::method_registers.vertex_textures[i], m_rtts); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index dad3560a7e..17420a4171 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -132,7 +132,6 @@ private: gl::vao m_vao; shared_mutex m_sampler_mutex; - u64 surface_store_tag = 0; atomic_t m_samplers_dirty = {true}; std::array, rsx::limits::fragment_textures_count> fs_sampler_state = {}; std::array, rsx::limits::vertex_textures_count> vs_sampler_state = {}; diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index 5d0a666b59..8527b826ef 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -964,7 +964,7 @@ namespace gl section.set_view_flags(flags); } - void insert_texture_barrier(gl::command_context&, gl::texture*) override + void insert_texture_barrier(gl::command_context&, gl::texture*, bool) override { auto &caps = gl::get_driver_caps(); diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 3e1d17e320..9684569c60 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -141,18 +141,21 @@ void VKGSRender::update_draw_state() void VKGSRender::load_texture_env() { - //Load textures - bool update_framebuffer_sourced = false; + // Load textures bool check_for_cyclic_refs = false; + auto check_surface_cache_sampler = [&](auto descriptor) + { + if (!m_texture_cache.test_if_descriptor_expired(*m_current_command_buffer, m_rtts, descriptor)) + { + check_for_cyclic_refs |= descriptor->is_cyclic_reference; + return true; + } + + return false; + }; std::lock_guard lock(m_sampler_mutex); - if (surface_store_tag != m_rtts.cache_tag) [[unlikely]] - { - update_framebuffer_sourced = true; - surface_store_tag = m_rtts.cache_tag; - } - for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i) { if (!(textures_ref & 1)) @@ -161,11 +164,9 @@ void VKGSRender::load_texture_env() if (!fs_sampler_state[i]) fs_sampler_state[i] = std::make_unique(); - if (m_samplers_dirty || m_textures_dirty[i] || - (update_framebuffer_sourced && fs_sampler_state[i]->upload_context == rsx::texture_upload_context::framebuffer_storage)) + auto sampler_state = static_cast(fs_sampler_state[i].get()); + if (m_samplers_dirty || m_textures_dirty[i] || !check_surface_cache_sampler(sampler_state)) { - auto sampler_state = static_cast(fs_sampler_state[i].get()); - if (rsx::method_registers.fragment_textures[i].enabled()) { check_heap_status(VK_HEAP_CHECK_TEXTURE_UPLOAD_STORAGE); @@ -301,11 +302,9 @@ void VKGSRender::load_texture_env() if (!vs_sampler_state[i]) vs_sampler_state[i] = std::make_unique(); - if (m_samplers_dirty || m_vertex_textures_dirty[i] || - (update_framebuffer_sourced && vs_sampler_state[i]->upload_context == rsx::texture_upload_context::framebuffer_storage)) + auto sampler_state = static_cast(vs_sampler_state[i].get()); + if (m_samplers_dirty || m_vertex_textures_dirty[i] || !check_surface_cache_sampler(sampler_state)) { - auto sampler_state = static_cast(vs_sampler_state[i].get()); - if (rsx::method_registers.vertex_textures[i].enabled()) { check_heap_status(VK_HEAP_CHECK_TEXTURE_UPLOAD_STORAGE); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 3752252fb5..9b1fa5d84d 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -371,7 +371,6 @@ private: u64 m_cond_render_sync_tag = 0; shared_mutex m_sampler_mutex; - u64 surface_store_tag = 0; atomic_t m_samplers_dirty = { true }; std::unique_ptr m_stencil_mirror_sampler; std::array, rsx::limits::fragment_textures_count> fs_sampler_state = {}; diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index cd7c694c4e..31471673f6 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -1300,8 +1300,14 @@ namespace vk section.set_view_flags(expected_flags); } - void insert_texture_barrier(vk::command_buffer& cmd, vk::image* tex) override + void insert_texture_barrier(vk::command_buffer& cmd, vk::image* tex, bool strong_ordering) override { + if (!strong_ordering && tex->current_layout == VK_IMAGE_LAYOUT_GENERAL) + { + // A previous barrier already exists, do nothing + return; + } + vk::as_rtt(tex)->texture_barrier(cmd); }