From bf234dc6685f922090d49c96861e14306b18567a Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 26 Oct 2017 16:20:09 +0300 Subject: [PATCH] rsx: Implement memory tags for strict mode to validate render target memory --- rpcs3/Emu/RSX/Common/texture_cache.h | 116 +++++++++++++++++++-------- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 14 ++-- rpcs3/Emu/RSX/GL/GLRenderTargets.cpp | 6 +- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 21 ++++- rpcs3/Emu/RSX/rsx_cache.h | 2 +- 5 files changed, 115 insertions(+), 44 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 918e92997b..08c599255b 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -186,6 +186,21 @@ namespace rsx private: //Internal implementation methods and helpers + utils::protection get_memory_protection(u32 address) + { + auto found = m_cache.find(get_block_address(address)); + if (found != m_cache.end()) + { + for (const auto &tex : found->second.data) + { + if (tex.is_locked() && tex.overlaps(address, false)) + return tex.get_protection(); + } + } + + return utils::protection::rw; + } + //Get intersecting set - Returns all objects intersecting a given range and their owning blocks std::vector> get_intersecting_set(u32 address, u32 range, bool check_whole_size) { @@ -723,50 +738,56 @@ namespace rsx //TODO: When framebuffer Y compression is properly handled, this section can be removed. A more accurate framebuffer storage check exists below this block if (auto texptr = m_rtts.get_texture_from_render_target_if_applicable(texaddr)) { - if (extended_dimension != rsx::texture_dimension_extended::texture_dimension_2d) - LOG_ERROR(RSX, "Texture resides in render target memory, but requested type is not 2D (%d)", (u32)extended_dimension); - - for (const auto& tex : m_rtts.m_bound_render_targets) + if (test_framebuffer(texaddr)) { - if (std::get<0>(tex) == texaddr) + if (extended_dimension != rsx::texture_dimension_extended::texture_dimension_2d) + LOG_ERROR(RSX, "Texture resides in render target memory, but requested type is not 2D (%d)", (u32)extended_dimension); + + for (const auto& tex : m_rtts.m_bound_render_targets) + { + if (std::get<0>(tex) == texaddr) + { + if (g_cfg.video.strict_rendering_mode) + { + LOG_WARNING(RSX, "Attempting to sample a currently bound render target @ 0x%x", texaddr); + return create_temporary_subresource_view(cmd, texptr, format, 0, 0, texptr->width(), texptr->height()); + } + else + { + //issue a texture barrier to ensure previous writes are visible + insert_texture_barrier(); + break; + } + } + } + + return texptr->get_view(); + } + } + + if (auto texptr = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr)) + { + if (test_framebuffer(texaddr)) + { + if (extended_dimension != rsx::texture_dimension_extended::texture_dimension_2d) + LOG_ERROR(RSX, "Texture resides in depth buffer memory, but requested type is not 2D (%d)", (u32)extended_dimension); + + if (texaddr == std::get<0>(m_rtts.m_bound_depth_stencil)) { if (g_cfg.video.strict_rendering_mode) { - LOG_WARNING(RSX, "Attempting to sample a currently bound render target @ 0x%x", texaddr); + LOG_WARNING(RSX, "Attempting to sample a currently bound depth surface @ 0x%x", texaddr); return create_temporary_subresource_view(cmd, texptr, format, 0, 0, texptr->width(), texptr->height()); } else { //issue a texture barrier to ensure previous writes are visible insert_texture_barrier(); - break; } } + + return texptr->get_view(); } - - return texptr->get_view(); - } - - if (auto texptr = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr)) - { - if (extended_dimension != rsx::texture_dimension_extended::texture_dimension_2d) - LOG_ERROR(RSX, "Texture resides in depth buffer memory, but requested type is not 2D (%d)", (u32)extended_dimension); - - if (texaddr == std::get<0>(m_rtts.m_bound_depth_stencil)) - { - if (g_cfg.video.strict_rendering_mode) - { - LOG_WARNING(RSX, "Attempting to sample a currently bound depth surface @ 0x%x", texaddr); - return create_temporary_subresource_view(cmd, texptr, format, 0, 0, texptr->width(), texptr->height()); - } - else - { - //issue a texture barrier to ensure previous writes are visible - insert_texture_barrier(); - } - } - - return texptr->get_view(); } } @@ -810,7 +831,7 @@ namespace rsx const u32 internal_width = (const u32)(tex_width * internal_scale); const auto rsc = m_rtts.get_surface_subresource_if_applicable(texaddr, internal_width, tex_height, tex_pitch, true); - if (rsc.surface) + if (rsc.surface && test_framebuffer(texaddr)) { //TODO: Check that this region is not cpu-dirty before doing a copy if (extended_dimension != rsx::texture_dimension_extended::texture_dimension_2d) @@ -1248,5 +1269,36 @@ namespace rsx { return m_texture_memory_in_use; } + + void tag_framebuffer(u32 texaddr) + { + if (!g_cfg.video.strict_rendering_mode) + return; + + switch (get_memory_protection(texaddr)) + { + case utils::protection::no: + return; + case utils::protection::ro: + LOG_ERROR(RSX, "Framebuffer memory occupied by regular texture!"); + return; + } + + vm::ps3::write32(texaddr, texaddr); + } + + bool test_framebuffer(u32 texaddr) + { + if (!g_cfg.video.strict_rendering_mode) + return true; + + if (g_cfg.video.write_color_buffers || g_cfg.video.write_depth_buffer) + { + if (get_memory_protection(texaddr) == utils::protection::no) + return true; + } + + return vm::ps3::read32(texaddr) == texaddr; + } }; } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 87a1ac3b18..404b687fd4 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -947,15 +947,17 @@ bool GLGSRender::check_program_state() if (!is_depth) surface = m_rtts.get_texture_from_render_target_if_applicable(texaddr); else - { surface = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr); - if (!surface && m_gl_texture_cache.is_depth_texture(texaddr, (u32)get_texture_size(tex))) - return std::make_tuple(true, 0); - } - - if (!surface) + const bool dirty_framebuffer = (surface != nullptr && !m_gl_texture_cache.test_framebuffer(texaddr)); + if (dirty_framebuffer || !surface) { + if (is_depth && m_gl_texture_cache.is_depth_texture(texaddr, (u32)get_texture_size(tex))) + return std::make_tuple(true, 0); + + if (dirty_framebuffer) + return std::make_tuple(false, 0); + auto rsc = m_rtts.get_surface_subresource_if_applicable(texaddr, 0, 0, tex.pitch()); if (!rsc.surface || rsc.is_depth_surface != is_depth) return std::make_tuple(false, 0); diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp index 4d539c4c20..e864ce7c53 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp @@ -224,7 +224,7 @@ void GLGSRender::init_buffers(bool skip_reading) //Verify pitch given is correct if pitch <= 64 (especially 64) if (pitchs[i] <= 64) - { + { const u16 native_pitch = std::get<1>(m_rtts.m_bound_render_targets[i])->get_native_pitch(); if (native_pitch > pitchs[i]) { @@ -236,6 +236,8 @@ void GLGSRender::init_buffers(bool skip_reading) surface_info[i].pitch = 0; } } + + m_gl_texture_cache.tag_framebuffer(surface_addresses[i]); } else surface_info[i] = {}; @@ -266,6 +268,8 @@ void GLGSRender::init_buffers(bool skip_reading) depth_surface_info.pitch = 0; } } + + m_gl_texture_cache.tag_framebuffer(depth_address); } else depth_surface_info = {}; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index c1c80707e1..cf60adc94a 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1819,14 +1819,23 @@ bool VKGSRender::check_program_status() if (!is_depth) surface = m_rtts.get_texture_from_render_target_if_applicable(texaddr); else - { surface = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr); - if (!surface && m_texture_cache.is_depth_texture(texaddr, (u32)get_texture_size(tex))) + const bool dirty_framebuffer = (surface != nullptr && !m_texture_cache.test_framebuffer(texaddr)); + if (dirty_framebuffer || !surface) + { + if (is_depth && m_texture_cache.is_depth_texture(texaddr, (u32)get_texture_size(tex))) return std::make_tuple(true, 0); - } - if (!surface) return std::make_tuple(false, 0); + if (dirty_framebuffer) + return std::make_tuple(false, 0); + + auto rsc = m_rtts.get_surface_subresource_if_applicable(texaddr, 0, 0, tex.pitch()); + if (!rsc.surface || rsc.is_depth_surface != is_depth) + return std::make_tuple(false, 0); + + surface = rsc.surface; + } return std::make_tuple(true, surface->native_pitch); }; @@ -2284,6 +2293,8 @@ void VKGSRender::prepare_rtts() //Ignore this buffer (usually set to 64) m_surface_info[index].pitch = 0; } + + m_texture_cache.tag_framebuffer(surface_addresses[index]); } if (std::get<0>(m_rtts.m_bound_depth_stencil) != 0) @@ -2297,6 +2308,8 @@ void VKGSRender::prepare_rtts() if (m_depth_surface_info.pitch <= 64 && clip_width > m_depth_surface_info.pitch) m_depth_surface_info.pitch = 0; + + m_texture_cache.tag_framebuffer(zeta_address); } m_draw_buffers_count = static_cast(draw_buffers.size()); diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 2f48faada0..1d0e9c9219 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -239,7 +239,7 @@ namespace rsx return std::make_pair(min, max); } - utils::protection get_protection() + utils::protection get_protection() const { return protection; }