diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index f91fc10556..c394f10f27 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -112,6 +112,9 @@ namespace rsx // Tags are tested in an X pattern for (const auto &tag : memory_tag_samples) { + if (!tag.first) + break; + if (tag.second != *reinterpret_cast(vm::g_sudo_addr + tag.first)) return false; } @@ -119,22 +122,59 @@ namespace rsx return true; } + template + void set_old_contents(T* other) + { + if (!other || other->get_rsx_pitch() != this->get_rsx_pitch()) + { + old_contents = nullptr; + return; + } + + old_contents = other; + } + void queue_tag(u32 address) { - const u32 pitch = get_native_pitch(); - const u32 memory_length = pitch * get_surface_height(); + for (int i = 0; i < memory_tag_samples.size(); ++i) + { + if (LIKELY(i)) + memory_tag_samples[i].first = 0; + else + memory_tag_samples[i].first = address; // Top left + } - memory_tag_samples[0].first = address; // Top left - memory_tag_samples[1].first = address + memory_length - 4; // Bottom right - memory_tag_samples[2].first = address + pitch; // Top right - memory_tag_samples[3].first = address + (memory_length / 2); // Center - memory_tag_samples[4].first = address + memory_length - pitch; // Bottom left + const u32 pitch = get_native_pitch(); + if (UNLIKELY(pitch < 16)) + { + // Not enough area to gather samples if pitch is too small + return; + } + + // Top right corner + memory_tag_samples[1].first = address + pitch - 8; + + if (const u32 h = get_surface_height(); h > 1) + { + // Last row + const u32 pitch2 = get_rsx_pitch(); + const u32 last_row_offset = pitch2 * (h - 1); + memory_tag_samples[2].first = address + last_row_offset; // Bottom left corner + memory_tag_samples[3].first = address + last_row_offset + pitch - 8; // Bottom right corner + + // Centroid + const u32 center_row_offset = pitch2 * (h / 2); + memory_tag_samples[4].first = address + center_row_offset + pitch / 2; + } } void sync_tag() { for (auto &tag : memory_tag_samples) { + if (!tag.first) + break; + tag.second = *reinterpret_cast(vm::g_sudo_addr + tag.first); } } @@ -364,7 +404,11 @@ namespace rsx surface_storage_type &rtt = It->second; if (Traits::rtt_has_format_width_height(rtt, color_format, width, height)) { - Traits::notify_surface_persist(rtt); + if (Traits::surface_is_pitch_compatible(rtt, pitch)) + Traits::notify_surface_persist(rtt); + else + Traits::invalidate_surface_contents(command_list, Traits::get(rtt), nullptr, address, pitch); + Traits::prepare_rtt_for_drawing(command_list, Traits::get(rtt)); return Traits::get(rtt); } @@ -401,7 +445,7 @@ namespace rsx invalidated_resources.erase(It); new_surface = Traits::get(new_surface_storage); - Traits::invalidate_surface_contents(address, command_list, new_surface, contents_to_copy); + Traits::invalidate_surface_contents(command_list, new_surface, contents_to_copy, address, pitch); Traits::prepare_rtt_for_drawing(command_list, new_surface); break; } @@ -421,7 +465,7 @@ namespace rsx return new_surface; } - m_render_targets_storage[address] = Traits::create_new_surface(address, color_format, width, height, contents_to_copy, std::forward(extra_params)...); + m_render_targets_storage[address] = Traits::create_new_surface(address, color_format, width, height, pitch, contents_to_copy, std::forward(extra_params)...); return Traits::get(m_render_targets_storage[address]); } @@ -456,7 +500,11 @@ namespace rsx surface_storage_type &ds = It->second; if (Traits::ds_has_format_width_height(ds, depth_format, width, height)) { - Traits::notify_surface_persist(ds); + if (Traits::surface_is_pitch_compatible(ds, pitch)) + Traits::notify_surface_persist(ds); + else + Traits::invalidate_surface_contents(command_list, Traits::get(ds), nullptr, address, pitch); + Traits::prepare_ds_for_drawing(command_list, Traits::get(ds)); return Traits::get(ds); } @@ -493,7 +541,7 @@ namespace rsx new_surface = Traits::get(new_surface_storage); Traits::prepare_ds_for_drawing(command_list, new_surface); - Traits::invalidate_surface_contents(address, command_list, new_surface, contents_to_copy); + Traits::invalidate_surface_contents(command_list, new_surface, contents_to_copy, address, pitch); break; } } @@ -512,7 +560,7 @@ namespace rsx return new_surface; } - m_depth_stencil_storage[address] = Traits::create_new_surface(address, depth_format, width, height, contents_to_copy, std::forward(extra_params)...); + m_depth_stencil_storage[address] = Traits::create_new_surface(address, depth_format, width, height, pitch, contents_to_copy, std::forward(extra_params)...); return Traits::get(m_depth_stencil_storage[address]); } public: diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 35aea96541..146ec52605 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -278,7 +278,7 @@ namespace rsx if (_v > max_y) max_y = _v; if (const auto _w = max_x - min_x, _h = max_y - min_y; - (_w * _h) >= target_area) + u32(_w * _h) >= target_area) { // Target area mostly covered, return success return true; diff --git a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h index 49259758f6..07ed7a9b79 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h @@ -24,7 +24,7 @@ struct render_target_traits static ComPtr create_new_surface( u32 address, - surface_color_format color_format, size_t width, size_t height, + surface_color_format color_format, size_t width, size_t height, size_t /*pitch*/, ID3D12Resource* /*old*/, ID3D12Device* device, const std::array &clear_color, float, u8) { @@ -85,7 +85,7 @@ struct render_target_traits static ComPtr create_new_surface( u32 address, - surface_depth_format surfaceDepthFormat, size_t width, size_t height, + surface_depth_format surfaceDepthFormat, size_t width, size_t height, size_t /*pitch*/, ID3D12Resource* /*old*/, ID3D12Device* device, const std::array& , float clear_depth, u8 clear_stencil) { @@ -130,9 +130,9 @@ struct render_target_traits static void invalidate_surface_contents( - u32, ID3D12GraphicsCommandList*, - ID3D12Resource*, ID3D12Resource*) + ID3D12Resource*, ID3D12Resource*, + u32, size_t) {} static @@ -143,6 +143,12 @@ struct render_target_traits void notify_surface_persist(const ComPtr&) {} + static + bool surface_is_pitch_compatible(const ComPtr&, size_t) + { + return true; + } + static bool rtt_has_format_width_height(const ComPtr &rtt, surface_color_format surface_color_format, size_t width, size_t height, bool=false) { diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index a01351ae3e..0c1f3c5aba 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1646,9 +1646,8 @@ void GLGSRender::flip(int buffer) { gl::command_context cmd = { gl_state }; const auto overlap_info = m_rtts.get_merged_texture_memory_region(cmd, absolute_address, buffer_width, buffer_height, buffer_pitch); - verify(HERE), !overlap_info.empty(); - if (overlap_info.back().surface == render_target_texture) + if (!overlap_info.empty() && overlap_info.back().surface == render_target_texture) { // Confirmed to be the newest data source in that range image = render_target_texture->raw_handle(); diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp index c3ae4f6415..d04dee4471 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp @@ -256,7 +256,7 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk auto rtt = std::get<1>(m_rtts.m_bound_render_targets[i]); color_targets[i] = rtt->id(); - rtt->set_rsx_pitch(layout.actual_color_pitch[i]); + verify("Pitch mismatch!" HERE), rtt->get_rsx_pitch() == layout.actual_color_pitch[i]; m_surface_info[i] = { layout.color_addresses[i], layout.actual_color_pitch[i], false, layout.color_format, layout.depth_format, layout.width, layout.height, color_bpp }; rtt->tile = find_tile(color_offsets[i], color_locations[i]); @@ -285,7 +285,7 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk auto ds = std::get<1>(m_rtts.m_bound_depth_stencil); depth_stencil_target = ds->id(); - std::get<1>(m_rtts.m_bound_depth_stencil)->set_rsx_pitch(layout.actual_zeta_pitch); + verify("Pitch mismatch!" HERE), std::get<1>(m_rtts.m_bound_depth_stencil)->get_rsx_pitch() == layout.actual_zeta_pitch; m_depth_surface_info = { layout.zeta_address, layout.actual_zeta_pitch, true, layout.color_format, layout.depth_format, layout.width, layout.height, depth_bpp }; ds->write_aa_mode = layout.aa_mode; diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.h b/rpcs3/Emu/RSX/GL/GLRenderTargets.h index 6f19e81f42..ea28c811bc 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.h +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.h @@ -166,8 +166,7 @@ struct gl_render_target_traits std::unique_ptr create_new_surface( u32 address, rsx::surface_color_format surface_color_format, - size_t width, - size_t height, + size_t width, size_t height, size_t pitch, gl::render_target* old_surface ) { @@ -177,10 +176,11 @@ struct gl_render_target_traits std::unique_ptr result(new gl::render_target(rsx::apply_resolution_scale((u16)width, true), rsx::apply_resolution_scale((u16)height, true), (GLenum)internal_fmt)); result->set_native_pitch((u16)width * format.channel_count * format.channel_size); + result->set_rsx_pitch((u16)pitch); std::array native_layout = { (GLenum)format.swizzle.a, (GLenum)format.swizzle.r, (GLenum)format.swizzle.g, (GLenum)format.swizzle.b }; result->set_native_component_layout(native_layout); - result->old_contents = old_surface; + result->set_old_contents(old_surface); result->set_cleared(false); result->update_surface(); @@ -192,8 +192,7 @@ struct gl_render_target_traits std::unique_ptr create_new_surface( u32 address, rsx::surface_depth_format surface_depth_format, - size_t width, - size_t height, + size_t width, size_t height, size_t pitch, gl::render_target* old_surface ) { @@ -207,8 +206,9 @@ struct gl_render_target_traits std::array native_layout = { GL_RED, GL_RED, GL_RED, GL_RED }; result->set_native_pitch(native_pitch); + result->set_rsx_pitch((u16)pitch); result->set_native_component_layout(native_layout); - result->old_contents = old_surface; + result->set_old_contents(old_surface); result->set_cleared(false); result->update_surface(); @@ -233,9 +233,16 @@ struct gl_render_target_traits static void prepare_ds_for_sampling(void *, gl::render_target*) {} static - void invalidate_surface_contents(u32 address, void *, gl::render_target *surface, gl::render_target* old_surface) + bool surface_is_pitch_compatible(const std::unique_ptr &surface, size_t pitch) { - surface->old_contents = old_surface; + return surface->get_rsx_pitch() == pitch; + } + + static + void invalidate_surface_contents(void *, gl::render_target *surface, gl::render_target* old_surface, u32 address, size_t pitch) + { + surface->set_rsx_pitch((u16)pitch); + surface->set_old_contents(old_surface); surface->reset_aa_mode(); surface->queue_tag(address); surface->set_cleared(false); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 28da25ca56..6320928d55 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -2923,7 +2923,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context) m_surface_info[index].address = layout.color_addresses[index]; m_surface_info[index].pitch = layout.actual_color_pitch[index]; - surface->rsx_pitch = layout.actual_color_pitch[index]; + verify("Pitch mismatch!" HERE), surface->rsx_pitch == layout.actual_color_pitch[index]; surface->write_aa_mode = layout.aa_mode; m_texture_cache.notify_surface_changed(layout.color_addresses[index]); @@ -2938,7 +2938,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context) m_depth_surface_info.address = layout.zeta_address; m_depth_surface_info.pitch = layout.actual_zeta_pitch; - ds->rsx_pitch = layout.actual_zeta_pitch; + verify("Pitch mismatch!" HERE), ds->rsx_pitch == layout.actual_zeta_pitch; ds->write_aa_mode = layout.aa_mode; m_texture_cache.notify_surface_changed(layout.zeta_address); @@ -3284,9 +3284,7 @@ void VKGSRender::flip(int buffer) else { const auto overlap_info = m_rtts.get_merged_texture_memory_region(*m_current_command_buffer, absolute_address, buffer_width, buffer_height, buffer_pitch); - verify(HERE), !overlap_info.empty(); - - if (overlap_info.back().surface == render_target_texture) + if (!overlap_info.empty() && overlap_info.back().surface == render_target_texture) { // Confirmed to be the newest data source in that range image_to_flip = render_target_texture; @@ -3324,7 +3322,7 @@ void VKGSRender::flip(int buffer) // Read from cell const auto range = utils::address_range::start_length(absolute_address, buffer_pitch * buffer_height); const u32 lookup_mask = rsx::texture_upload_context::blit_engine_dst | rsx::texture_upload_context::framebuffer_storage; - const auto overlap = m_texture_cache.find_texture_from_range(range, 0, lookup_mask); + const auto overlap = m_texture_cache.find_texture_from_range(range, 0, lookup_mask); bool flush_queue = false; for (const auto & section : overlap) diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index c85cc94bde..824b25ba3e 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -175,7 +175,7 @@ namespace rsx static std::unique_ptr create_new_surface( u32 address, surface_color_format format, - size_t width, size_t height, + size_t width, size_t height, size_t pitch, vk::render_target* old_surface, vk::render_device &device, vk::command_buffer *cmd) { @@ -197,10 +197,11 @@ namespace rsx change_image_layout(*cmd, rtt.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT)); rtt->native_component_map = fmt.second; + rtt->rsx_pitch = (u16)pitch; rtt->native_pitch = (u16)width * get_format_block_size_in_bytes(format); rtt->surface_width = (u16)width; rtt->surface_height = (u16)height; - rtt->old_contents = old_surface; + rtt->set_old_contents(old_surface); rtt->queue_tag(address); rtt->dirty = true; @@ -210,7 +211,7 @@ namespace rsx static std::unique_ptr create_new_surface( u32 address, surface_depth_format format, - size_t width, size_t height, + size_t width, size_t height, size_t pitch, vk::render_target* old_surface, vk::render_device &device, vk::command_buffer *cmd) { @@ -242,17 +243,17 @@ namespace rsx ds->native_pitch *= 2; ds->attachment_aspect_flag = range.aspectMask; + ds->rsx_pitch = (u16)pitch; ds->surface_width = (u16)width; ds->surface_height = (u16)height; - ds->old_contents = old_surface; + ds->set_old_contents(old_surface); ds->queue_tag(address); ds->dirty = true; return ds; } - static - void get_surface_info(vk::render_target *surface, rsx::surface_format_info *info) + static void get_surface_info(vk::render_target *surface, rsx::surface_format_info *info) { info->rsx_pitch = surface->rsx_pitch; info->native_pitch = surface->native_pitch; @@ -293,24 +294,27 @@ namespace rsx change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range); } - static - void invalidate_surface_contents(u32 address, vk::command_buffer* /*pcmd*/, vk::render_target *surface, vk::render_target *old_surface) + static bool surface_is_pitch_compatible(const std::unique_ptr &surface, size_t pitch) { - surface->old_contents = old_surface; + return surface->rsx_pitch == pitch; + } + + static void invalidate_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *surface, vk::render_target *old_surface, u32 address, size_t pitch) + { + surface->rsx_pitch = (u16)pitch; + surface->set_old_contents(old_surface); surface->reset_aa_mode(); surface->queue_tag(address); surface->dirty = true; } - static - void notify_surface_invalidated(const std::unique_ptr &surface) + static void notify_surface_invalidated(const std::unique_ptr &surface) { surface->frame_tag = vk::get_current_frame_id(); if (!surface->frame_tag) surface->frame_tag = 1; } - static - void notify_surface_persist(const std::unique_ptr &surface) + static void notify_surface_persist(const std::unique_ptr &surface) { surface->save_aa_mode(); }