From 12ab03b0b591d6daf9b8d0ecb694e2ff88e9b8c6 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 26 Sep 2017 16:24:43 +0300 Subject: [PATCH] rsx/gl: Implement resolution scaling rsx: Revise wpos calculation to take resolution scale into account --- Utilities/geometry.h | 4 +- rpcs3/Emu/RSX/Common/GLSLCommon.h | 6 ++ rpcs3/Emu/RSX/Common/ProgramStateCache.h | 71 +++++++++++-------- rpcs3/Emu/RSX/Common/texture_cache.h | 14 +++- .../D3D12/D3D12FragmentProgramDecompiler.cpp | 8 ++- rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp | 7 +- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 12 ++-- rpcs3/Emu/RSX/GL/GLRenderTargets.h | 35 ++++++--- rpcs3/Emu/RSX/RSXFragmentProgram.h | 2 +- rpcs3/Emu/RSX/RSXThread.cpp | 42 ++++++++--- rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp | 7 +- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 39 +++++----- rpcs3/Emu/RSX/VK/VKRenderTargets.h | 27 ++++--- rpcs3/Emu/RSX/rsx_utils.h | 26 +++++++ 14 files changed, 202 insertions(+), 98 deletions(-) diff --git a/Utilities/geometry.h b/Utilities/geometry.h index f488f4ead0..6e824e981d 100644 --- a/Utilities/geometry.h +++ b/Utilities/geometry.h @@ -731,9 +731,9 @@ struct area_base { return{ x1 * size.width, y1 * size.height, x2 * size.width, y2 * size.height }; } - constexpr area_base operator * (const T& value) const + constexpr area_base operator * (const f32& value) const { - return{ x1 * value, y1 * value, x2 * value, y2 * value }; + return{ (T)(x1 * value), (T)(y1 * value), (T)(x2 * value), (T)(y2 * value) }; } template diff --git a/rpcs3/Emu/RSX/Common/GLSLCommon.h b/rpcs3/Emu/RSX/Common/GLSLCommon.h index 5de0effc94..a93ee5a70a 100644 --- a/rpcs3/Emu/RSX/Common/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Common/GLSLCommon.h @@ -338,6 +338,12 @@ namespace glsl OS << "{\n"; OS << " return decodeLinearDepth(texture(tex, coord.xy).r);\n"; OS << "}\n\n"; + + OS << "vec4 get_wpos()\n"; + OS << "{\n"; + OS << " float abs_scale = abs(wpos_scale);\n"; + OS << " return (gl_FragCoord * vec4(abs_scale, wpos_scale, 1., 1.)) + vec4(0., wpos_bias, 0., 0.);\n"; + OS << "}\n\n"; } static void insert_fog_declaration(std::ostream& OS) diff --git a/rpcs3/Emu/RSX/Common/ProgramStateCache.h b/rpcs3/Emu/RSX/Common/ProgramStateCache.h index cd28b04dd7..b3cb6562a3 100644 --- a/rpcs3/Emu/RSX/Common/ProgramStateCache.h +++ b/rpcs3/Emu/RSX/Common/ProgramStateCache.h @@ -167,6 +167,9 @@ public: f32 fp_value; }; + program_buffer_patch_entry() + {} + program_buffer_patch_entry(f32& key, f32& value) { fp_key = key; @@ -178,25 +181,43 @@ public: hex_key = key; hex_value = value; } + + bool test_and_set(f32 value, f32* dst) const + { + u32 hex = (u32&)value; + if ((hex & 0x7FFFFFFF) == (hex_key & 0x7FFFFFFF)) + { + hex = (hex & ~0x7FFFFFF) | hex_value; + *dst = (f32&)hex; + return true; + } + + return false; + } }; struct { - std::vector keys; + std::unordered_map db; void add(program_buffer_patch_entry& e) { - keys.push_back(e); + db[e.fp_key] = e; + } + + void add(f32& key, f32& value) + { + db[key] = { key, value }; } void clear() { - keys.resize(0); + db.clear(); } bool is_empty() const { - return keys.size() == 0; + return db.size() == 0; } } patch_table; @@ -287,49 +308,43 @@ public: verify(HERE), (dst_buffer.size_bytes() >= ::narrow(I->second.FragmentConstantOffsetCache.size()) * 16); - size_t offset = 0; - if (patch_table.is_empty()) + f32* dst = dst_buffer.data(); + f32 tmp[4]; + for (size_t offset_in_fragment_program : I->second.FragmentConstantOffsetCache) { - for (size_t offset_in_fragment_program : I->second.FragmentConstantOffsetCache) + void *data = (char*)fragment_program.addr + (u32)offset_in_fragment_program; + const __m128i &vector = _mm_loadu_si128((__m128i*)data); + const __m128i &shuffled_vector = _mm_shuffle_epi8(vector, mask); + + if (!patch_table.is_empty()) { - void *data = (char*)fragment_program.addr + (u32)offset_in_fragment_program; - const __m128i &vector = _mm_loadu_si128((__m128i*)data); - const __m128i &shuffled_vector = _mm_shuffle_epi8(vector, mask); - _mm_stream_si128((__m128i*)dst_buffer.subspan(offset, 4).data(), shuffled_vector); - offset += 4; - } - } - else - { - for (size_t offset_in_fragment_program : I->second.FragmentConstantOffsetCache) - { - void *data = (char*)fragment_program.addr + (u32)offset_in_fragment_program; - f32* src = (f32*)data; - f32* dst = dst_buffer.subspan(offset, 4).data(); + _mm_storeu_ps(tmp, (__m128&)shuffled_vector); bool patched; for (int i = 0; i < 4; ++i) { patched = false; - for (auto& e : patch_table.keys) + for (auto& e : patch_table.db) { //TODO: Use fp comparison with fabsf without hurting performance - if (e.hex_key == (u32&)src[i]) + if (patched = e.second.test_and_set(tmp[i], &dst[i])) { - dst[i] = e.fp_value; - patched = true; break; } } if (!patched) { - dst[i] = src[i]; + dst[i] = tmp[i]; } } - - offset += 4; } + else + { + _mm_stream_si128((__m128i*)dst, shuffled_vector); + } + + dst += 4; } } diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 06cdc69764..8d9ca166b5 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -856,13 +856,14 @@ namespace rsx return rsc.surface->get_view(); } - else - return create_temporary_subresource_view(cmd, rsc.surface, format, rsc.x, rsc.y, rsc.w, rsc.h); + else return create_temporary_subresource_view(cmd, rsc.surface, format, rsx::apply_resolution_scale(rsc.x, false), rsx::apply_resolution_scale(rsc.y, false), + rsx::apply_resolution_scale(rsc.w, true), rsx::apply_resolution_scale(rsc.h, true)); } else { LOG_WARNING(RSX, "Attempting to sample a currently bound render target @ 0x%x", texaddr); - return create_temporary_subresource_view(cmd, rsc.surface, format, rsc.x, rsc.y, rsc.w, rsc.h); + return create_temporary_subresource_view(cmd, rsc.surface, format, rsx::apply_resolution_scale(rsc.x, false), rsx::apply_resolution_scale(rsc.y, false), + rsx::apply_resolution_scale(rsc.w, true), rsx::apply_resolution_scale(rsc.h, true)); } } } @@ -1198,6 +1199,13 @@ namespace rsx default_remap_vector)->get_raw_texture(); } + const f32 scale = rsx::get_resolution_scale(); + if (src_is_render_target) + src_area = src_area * scale; + + if (dst_is_render_target) + dst_area = dst_area * scale; + blitter.scale_image(vram_texture, dest_texture, src_area, dst_area, interpolate, is_depth_blit); return true; } diff --git a/rpcs3/Emu/RSX/D3D12/D3D12FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/D3D12/D3D12FragmentProgramDecompiler.cpp index 40c5ebad58..c3b738949d 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12FragmentProgramDecompiler.cpp @@ -46,8 +46,8 @@ void D3D12FragmentDecompiler::insertHeader(std::stringstream & OS) OS << " float alpha_ref;\n"; OS << " uint alpha_func;\n"; OS << " uint fog_mode;\n"; - OS << " uint window_origin;\n"; - OS << " uint window_height;\n"; + OS << " float wpos_scale;\n"; + OS << " float wpos_bias;\n"; OS << " float4 texture_parameters[16];\n"; OS << "};\n"; } @@ -224,8 +224,10 @@ void D3D12FragmentDecompiler::insertMainStart(std::stringstream & OS) } } + //NOTE: Framebuffer scaling not actually supported. wpos_scale is used to reconstruct the true window height OS << " float4 wpos = In.Position;\n"; - OS << " if (window_origin != 0) wpos.y = window_height - wpos.y;\n"; + OS << " float4 res_scale = abs(1.f / wpos_scale);\n"; + OS << " if (wpos_scale < 0) wpos.y = (wpos_bias * res_scale) - wpos.y;\n"; OS << " float4 ssa = is_front_face ? float4(1., 1., 1., 1.) : float4(-1., -1., -1., -1.);\n"; // Declare output diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index e0e6ec5708..32fc25283a 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -154,8 +154,8 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS) OS << " float alpha_ref;\n"; OS << " uint alpha_func;\n"; OS << " uint fog_mode;\n"; - OS << " uint window_origin;\n"; - OS << " uint window_height;\n"; + OS << " float wpos_scale;\n"; + OS << " float wpos_bias;\n"; OS << " vec4 texture_parameters[16];\n"; //sampling: x,y scaling and (unused) offsets data OS << "};\n"; } @@ -251,8 +251,7 @@ void GLFragmentDecompilerThread::insertMainStart(std::stringstream & OS) } OS << " vec4 ssa = gl_FrontFacing ? vec4(1.) : vec4(-1.);\n"; - OS << " vec4 wpos = gl_FragCoord;\n"; - OS << " if (window_origin != 0) wpos.y = window_height - wpos.y;\n"; + OS << " vec4 wpos = get_wpos();\n"; for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM]) { diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index fcd491e813..0f2e2f4c25 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -589,14 +589,14 @@ void GLGSRender::end() void GLGSRender::set_viewport() { //NOTE: scale offset matrix already contains the viewport transformation - const auto clip_width = rsx::method_registers.surface_clip_width(); - const auto clip_height = rsx::method_registers.surface_clip_height(); + const auto clip_width = rsx::apply_resolution_scale(rsx::method_registers.surface_clip_width(), true); + const auto clip_height = rsx::apply_resolution_scale(rsx::method_registers.surface_clip_height(), true); glViewport(0, 0, clip_width, clip_height); - u16 scissor_x = rsx::method_registers.scissor_origin_x(); - u16 scissor_w = rsx::method_registers.scissor_width(); - u16 scissor_y = rsx::method_registers.scissor_origin_y(); - u16 scissor_h = rsx::method_registers.scissor_height(); + u16 scissor_x = rsx::apply_resolution_scale(rsx::method_registers.scissor_origin_x(), false); + u16 scissor_w = rsx::apply_resolution_scale(rsx::method_registers.scissor_width(), true); + u16 scissor_y = rsx::apply_resolution_scale(rsx::method_registers.scissor_origin_y(), false); + u16 scissor_h = rsx::apply_resolution_scale(rsx::method_registers.scissor_height(), true); //Do not bother drawing anything if output is zero sized //TODO: Clip scissor region diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.h b/rpcs3/Emu/RSX/GL/GLRenderTargets.h index c69897173b..ee3c16cb08 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.h +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.h @@ -3,6 +3,7 @@ #include "GLHelpers.h" #include "stdafx.h" #include "../RSXThread.h" +#include "../rsx_utils.h" struct color_swizzle { @@ -52,12 +53,14 @@ namespace gl { bool is_cleared = false; - u32 rsx_pitch = 0; - u16 native_pitch = 0; + u32 rsx_pitch = 0; + u16 native_pitch = 0; - u16 surface_height = 0; - u16 surface_width = 0; - u16 surface_pixel_size = 0; + u16 internal_width = 0; + u16 internal_height = 0; + u16 surface_height = 0; + u16 surface_width = 0; + u16 surface_pixel_size = 0; texture::internal_format compatible_internal_format = texture::internal_format::rgba8; @@ -130,8 +133,16 @@ namespace gl void update_surface() { - surface_width = width(); - surface_height = height(); + internal_width = width(); + internal_height = height(); + surface_width = rsx::apply_inverse_resolution_scale(internal_width, true); + surface_height = rsx::apply_inverse_resolution_scale(internal_height, true); + } + + bool matches_dimensions(u16 _width, u16 _height) const + { + //Use foward scaling to account for rounding and clamping errors + return (rsx::apply_resolution_scale(_width, true) == internal_width) && (rsx::apply_resolution_scale(_height, true) == internal_height); } }; } @@ -162,7 +173,7 @@ struct gl_render_target_traits result->set_compatible_format(internal_fmt); __glcheck result->config() - .size({ (int)width, (int)height }) + .size({ (int)rsx::apply_resolution_scale((u16)width, true), (int)rsx::apply_resolution_scale((u16)height, true) }) .type(format.type) .format(format.format) .internal_format(internal_fmt) @@ -195,8 +206,10 @@ struct gl_render_target_traits auto format = rsx::internals::surface_depth_format_to_gl(surface_depth_format); result->recreate(gl::texture::target::texture2D); + const auto scale = rsx::get_resolution_scale(); + __glcheck result->config() - .size({ (int)width, (int)height }) + .size({ (int)rsx::apply_resolution_scale((u16)width, true), (int)rsx::apply_resolution_scale((u16)height, true) }) .type(format.type) .format(format.format) .internal_format(format.internal_format) @@ -247,7 +260,7 @@ struct gl_render_target_traits return false; auto internal_fmt = rsx::internals::sized_internal_format(format); - return rtt->get_compatible_internal_format() == internal_fmt && rtt->width() == width && rtt->height() == height; + return rtt->get_compatible_internal_format() == internal_fmt && rtt->matches_dimensions((u16)width, (u16)height); } static @@ -257,7 +270,7 @@ struct gl_render_target_traits return false; // TODO: check format - return rtt->width() == width && rtt->height() == height; + return rtt->matches_dimensions((u16)width, (u16)height); } // Note : pbo breaks fbo here so use classic texture copy diff --git a/rpcs3/Emu/RSX/RSXFragmentProgram.h b/rpcs3/Emu/RSX/RSXFragmentProgram.h index 8c0623dc4c..645aa0446c 100644 --- a/rpcs3/Emu/RSX/RSXFragmentProgram.h +++ b/rpcs3/Emu/RSX/RSXFragmentProgram.h @@ -230,7 +230,7 @@ struct RSXFragmentProgram bool front_color_specular_output : 1; u32 texture_dimensions; - float texture_pitch_scale[16]; + std::array texture_scale[16]; u8 textures_alpha_kill[16]; u8 textures_zfunc[16]; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 149c083df8..4ed3a2e159 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -8,11 +8,13 @@ #include "Common/BufferUtils.h" #include "rsx_methods.h" +#include "rsx_utils.h" #include "Utilities/GSL.h" #include "Utilities/StrUtil.h" #include +#include class GSRender; @@ -391,6 +393,9 @@ namespace rsx // Raise priority above other threads thread_ctrl::set_native_priority(1); + // Round to nearest to deal with forward/reverse scaling + fesetround(FE_TONEAREST); + // Deferred calls are used to batch draws together u32 deferred_primitive_type = 0; u32 deferred_call_size = 0; @@ -790,24 +795,33 @@ namespace rsx void thread::fill_fragment_state_buffer(void *buffer, const RSXFragmentProgram &fragment_program) { const u32 is_alpha_tested = rsx::method_registers.alpha_test_enabled(); - const float alpha_ref = rsx::method_registers.alpha_ref() / 255.f; + const f32 alpha_ref = rsx::method_registers.alpha_ref() / 255.f; const f32 fog0 = rsx::method_registers.fog_params_0(); const f32 fog1 = rsx::method_registers.fog_params_1(); const u32 alpha_func = static_cast(rsx::method_registers.alpha_func()); const u32 fog_mode = static_cast(rsx::method_registers.fog_equation()); - const u32 window_origin = static_cast(rsx::method_registers.shader_window_origin()); + + // Generate wpos coeffecients + // wpos equation is now as follows: + // wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0) + // wpos.x = (frag_coord / resolution_scale) + // wpos.zw = frag_coord.zw + + const auto window_origin = rsx::method_registers.shader_window_origin(); const u32 window_height = rsx::method_registers.shader_window_height(); - const float one = 1.f; + const f32 resolution_scale = rsx::get_resolution_scale(); + const f32 wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale); + const f32 wpos_bias = (window_origin == rsx::window_origin::top) ? 0.f : window_height; u32 *dst = static_cast(buffer); stream_vector(dst, (u32&)fog0, (u32&)fog1, is_alpha_tested, (u32&)alpha_ref); - stream_vector(dst + 4, alpha_func, fog_mode, window_origin, window_height); + stream_vector(dst + 4, alpha_func, fog_mode, (u32&)wpos_scale, (u32&)wpos_bias); size_t offset = 8; for (int index = 0; index < 16; ++index) { - stream_vector(&dst[offset], (u32&)fragment_program.texture_pitch_scale[index], (u32&)one, 0U, 0U); + stream_vector(&dst[offset], (u32&)fragment_program.texture_scale[index][0], (u32&)fragment_program.texture_scale[index][1], 0U, 0U); offset += 4; } } @@ -1305,10 +1319,13 @@ namespace rsx result.shadow_textures = 0; std::array texture_dimensions; + const auto resolution_scale = rsx::get_resolution_scale(); + for (u32 i = 0; i < rsx::limits::fragment_textures_count; ++i) { auto &tex = rsx::method_registers.fragment_textures[i]; - result.texture_pitch_scale[i] = 1.f; + result.texture_scale[i][0] = 1.f; + result.texture_scale[i][1] = 1.f; result.textures_alpha_kill[i] = 0; result.textures_zfunc[i] = 0; @@ -1345,14 +1362,23 @@ namespace rsx if (surface_exists && surface_pitch) { if (raw_format & CELL_GCM_TEXTURE_UN) - result.texture_pitch_scale[i] = (float)surface_pitch / tex.pitch(); + { + result.texture_scale[i][0] = (resolution_scale * (float)surface_pitch) / tex.pitch(); + result.texture_scale[i][1] = resolution_scale; + } } else { std::tie(surface_exists, surface_pitch) = get_surface_info(texaddr, tex, true); if (surface_exists) { - u32 format = raw_format & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); + if (raw_format & CELL_GCM_TEXTURE_UN) + { + result.texture_scale[i][0] = (resolution_scale * (float)surface_pitch) / tex.pitch(); + result.texture_scale[i][1] = resolution_scale; + } + + const u32 format = raw_format & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); switch (format) { case CELL_GCM_TEXTURE_A8R8G8B8: diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index 0559d13039..84b7a60159 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -166,8 +166,8 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS) OS << " float alpha_ref;\n"; OS << " uint alpha_func;\n"; OS << " uint fog_mode;\n"; - OS << " uint window_origin;\n"; - OS << " uint window_height;\n"; + OS << " float wpos_scale;\n"; + OS << " float wpos_bias;\n"; OS << " vec4 texture_parameters[16];\n"; OS << "};\n"; @@ -253,8 +253,7 @@ void VKFragmentDecompilerThread::insertMainStart(std::stringstream & OS) } OS << " vec4 ssa = gl_FrontFacing ? vec4(1.) : vec4(-1.);\n"; - OS << " vec4 wpos = gl_FragCoord;\n"; - OS << " if (window_origin != 0) wpos.y = window_height - wpos.y;\n"; + OS << " vec4 wpos = get_wpos();\n"; bool two_sided_enabled = m_prog.front_back_color_enabled && (m_prog.back_color_diffuse_output || m_prog.back_color_specular_output); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index f4d2528ced..aae74f35a5 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1266,17 +1266,19 @@ void VKGSRender::end() void VKGSRender::set_viewport() { - u16 scissor_x = rsx::method_registers.scissor_origin_x(); - u16 scissor_w = rsx::method_registers.scissor_width(); - u16 scissor_y = rsx::method_registers.scissor_origin_y(); - u16 scissor_h = rsx::method_registers.scissor_height(); + const auto clip_width = rsx::apply_resolution_scale(rsx::method_registers.surface_clip_width(), true); + const auto clip_height = rsx::apply_resolution_scale(rsx::method_registers.surface_clip_height(), true); + u16 scissor_x = rsx::apply_resolution_scale(rsx::method_registers.scissor_origin_x(), false); + u16 scissor_w = rsx::apply_resolution_scale(rsx::method_registers.scissor_width(), true); + u16 scissor_y = rsx::apply_resolution_scale(rsx::method_registers.scissor_origin_y(), false); + u16 scissor_h = rsx::apply_resolution_scale(rsx::method_registers.scissor_height(), true); //NOTE: The scale_offset matrix already has viewport matrix factored in VkViewport viewport = {}; viewport.x = 0; viewport.y = 0; - viewport.width = rsx::method_registers.surface_clip_width(); - viewport.height = rsx::method_registers.surface_clip_height(); + viewport.width = clip_width; + viewport.height = clip_height; viewport.minDepth = 0.f; viewport.maxDepth = 1.f; @@ -1341,13 +1343,14 @@ void VKGSRender::clear_surface(u32 mask) std::vector clear_descriptors; VkClearValue depth_stencil_clear_values, color_clear_values; - u16 scissor_x = rsx::method_registers.scissor_origin_x(); - u16 scissor_w = rsx::method_registers.scissor_width(); - u16 scissor_y = rsx::method_registers.scissor_origin_y(); - u16 scissor_h = rsx::method_registers.scissor_height(); + const auto scale = rsx::get_resolution_scale(); + u16 scissor_x = rsx::apply_resolution_scale(rsx::method_registers.scissor_origin_x(), false); + u16 scissor_w = rsx::apply_resolution_scale(rsx::method_registers.scissor_width(), true); + u16 scissor_y = rsx::apply_resolution_scale(rsx::method_registers.scissor_origin_y(), false); + u16 scissor_h = rsx::apply_resolution_scale(rsx::method_registers.scissor_height(), true); - const u32 fb_width = m_draw_fbo->width(); - const u32 fb_height = m_draw_fbo->height(); + const u16 fb_width = m_draw_fbo->width(); + const u16 fb_height = m_draw_fbo->height(); //clip region std::tie(scissor_x, scissor_y, scissor_w, scissor_h) = rsx::clip_region(fb_width, fb_height, scissor_x, scissor_y, scissor_w, scissor_h, true); @@ -2086,14 +2089,14 @@ void VKGSRender::prepare_rtts() const u32 surface_pitchs[] = { rsx::method_registers.surface_a_pitch(), rsx::method_registers.surface_b_pitch(), rsx::method_registers.surface_c_pitch(), rsx::method_registers.surface_d_pitch() }; + const auto fbo_width = rsx::apply_resolution_scale(clip_width, true); + const auto fbo_height = rsx::apply_resolution_scale(clip_height, true); + if (m_draw_fbo) { - const u32 fb_width = m_draw_fbo->width(); - const u32 fb_height = m_draw_fbo->height(); - bool really_changed = false; - if (fb_width == clip_width && fb_height == clip_height) + if (m_draw_fbo->width() == fbo_width && m_draw_fbo->height() == clip_height) { for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i) { @@ -2215,7 +2218,7 @@ void VKGSRender::prepare_rtts() for (auto &fbo : m_framebuffers_to_clean) { - if (fbo->matches(bound_images, clip_width, clip_height)) + if (fbo->matches(bound_images, fbo_width, fbo_height)) { m_draw_fbo.swap(fbo); m_draw_fbo->reset_refs(); @@ -2263,7 +2266,7 @@ void VKGSRender::prepare_rtts() if (m_draw_fbo) m_framebuffers_to_clean.push_back(std::move(m_draw_fbo)); - m_draw_fbo.reset(new vk::framebuffer_holder(*m_device, current_render_pass, clip_width, clip_height, std::move(fbo_images))); + m_draw_fbo.reset(new vk::framebuffer_holder(*m_device, current_render_pass, fbo_width, fbo_height, std::move(fbo_images))); } } diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index f6acbd36a0..8fd7052cb9 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -6,6 +6,7 @@ #include "../Common/surface_store.h" #include "../Common/TextureUtils.h" #include "VKFormats.h" +#include "../rsx_utils.h" struct ref_counted { @@ -59,12 +60,12 @@ namespace vk u16 get_surface_width() const override { - return width(); + return rsx::apply_inverse_resolution_scale(width(), true); } u16 get_surface_height() const override { - return height(); + return rsx::apply_inverse_resolution_scale(height(), true); } u16 get_rsx_pitch() const override @@ -76,6 +77,12 @@ namespace vk { return native_pitch; } + + bool matches_dimensions(u16 _width, u16 _height) const + { + //Use foward scaling to account for rounding and clamping errors + return (rsx::apply_resolution_scale(_width, true) == width()) && (rsx::apply_resolution_scale(_height, true) == height()); + } }; struct framebuffer_holder: public vk::framebuffer, public ref_counted @@ -114,7 +121,7 @@ namespace rsx VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TYPE_2D, requested_format, - static_cast(width), static_cast(height), 1, 1, 1, + static_cast(rsx::apply_resolution_scale((u16)width, true)), static_cast(rsx::apply_resolution_scale((u16)height, true)), 1, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, @@ -158,12 +165,14 @@ namespace rsx if (requested_format != VK_FORMAT_D16_UNORM) range.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + const auto scale = rsx::get_resolution_scale(); + std::unique_ptr ds; ds.reset(new vk::render_target(device, mem_mapping.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TYPE_2D, requested_format, - static_cast(width), static_cast(height), 1, 1, 1, + static_cast(rsx::apply_resolution_scale((u16)width, true)), static_cast(rsx::apply_resolution_scale((u16)height, true)), 1, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, @@ -202,8 +211,8 @@ namespace rsx { info->rsx_pitch = surface->rsx_pitch; info->native_pitch = surface->native_pitch; - info->surface_width = surface->info.extent.width; - info->surface_height = surface->info.extent.height; + info->surface_width = surface->get_surface_width(); + info->surface_height = surface->get_surface_height(); info->bpp = static_cast(info->native_pitch / info->surface_width); } @@ -260,8 +269,7 @@ namespace rsx VkFormat fmt = vk::get_compatible_surface_format(format).first; if (rtt->info.format == fmt && - rtt->info.extent.width == width && - rtt->info.extent.height == height) + rtt->matches_dimensions((u16)width, (u16)height)) return true; return false; @@ -272,8 +280,7 @@ namespace rsx if (check_refs && ds->deref_count == 0) //Surface may still have read refs from data 'copy' return false; - if (ds->info.extent.width == width && - ds->info.extent.height == height) + if (ds->matches_dimensions((u16)width, (u16)height)) { //Check format switch (ds->info.format) diff --git a/rpcs3/Emu/RSX/rsx_utils.h b/rpcs3/Emu/RSX/rsx_utils.h index e29af6db59..f361272479 100644 --- a/rpcs3/Emu/RSX/rsx_utils.h +++ b/rpcs3/Emu/RSX/rsx_utils.h @@ -207,4 +207,30 @@ namespace rsx return std::make_tuple(x, y, width, height); } + + static inline f32 get_resolution_scale() + { + return g_cfg.video.strict_rendering_mode? 1.f : ((f32)g_cfg.video.resolution_scale_percent / 100.f); + } + + static inline int get_resolution_scale_percent() + { + return g_cfg.video.strict_rendering_mode ? 100 : g_cfg.video.resolution_scale_percent; + } + + static inline const u16 apply_resolution_scale(u16 value, bool clamp) + { + if (clamp) + return (u16)std::max((get_resolution_scale_percent() * value) / 100, 1); + else + return (get_resolution_scale_percent() * value) / 100; + } + + static inline const u16 apply_inverse_resolution_scale(u16 value, bool clamp) + { + if (clamp) + return (u16)std::max((value * 100) / get_resolution_scale_percent(), 1); + else + return (value * 100) / get_resolution_scale_percent(); + } }