From 7973b8ac6d5ec8b488e2bcf87769d082f7298888 Mon Sep 17 00:00:00 2001 From: Ani Date: Tue, 18 Aug 2026 20:46:46 +0200 Subject: [PATCH 01/17] windows: Fix clang x64/arm64 builds --- rpcs3/rpcs3.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 1f58c9bc7..c3af28578 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -32,6 +32,7 @@ #include "module_verifier.hpp" #include "util/dyn_lib.hpp" #include +#include // TODO(cjj19970505@live.cn) // When compiling with WIN32_LEAN_AND_MEAN definition From ddd82ecada385db436f77bed21ffca46da5d008b Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Wed, 19 Aug 2026 08:15:27 +0200 Subject: [PATCH 02/17] =?UTF-8?q?Make=20optional=20VSH=20on=20File=20?= =?UTF-8?q?=E2=9E=94=20All=20Titles=20=E2=9E=94=20Create=20LLVM=20Caches?= =?UTF-8?q?=20dialog=20(#19270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make optional VSH CPU compilation on `File ➔ All Titles ➔ Create LLVM Caches` dialog. By default it is now excluded due to VSH caches are fully covered by the dedicated submenu `File ➔ Firmware`. --- rpcs3/rpcs3qt/game_list_actions.cpp | 37 +++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/rpcs3/rpcs3qt/game_list_actions.cpp b/rpcs3/rpcs3qt/game_list_actions.cpp index 6a1c737fc..9e2cf9d06 100644 --- a/rpcs3/rpcs3qt/game_list_actions.cpp +++ b/rpcs3/rpcs3qt/game_list_actions.cpp @@ -1235,23 +1235,46 @@ void game_list_actions::BatchActionBySerials(progress_dialog* pdlg, const std::s void game_list_actions::BatchCreateCPUCaches(const std::vector& games, bool is_fast_compilation, bool is_interactive) { - if (is_interactive && QMessageBox::question(m_game_list_frame, tr("Confirm Creation"), tr("Create LLVM cache?")) != QMessageBox::Yes) + // The VSH cache is only part of this batch if no specific games were selected + const bool vsh_selectable = games.empty(); + + // Without a dialog to ask the user, a batch of all titles includes the VSH cache + bool include_vsh = vsh_selectable; + + if (is_interactive) { - return; + QMessageBox mb(QMessageBox::Question, tr("Confirm Creation"), tr("Create LLVM cache?"), QMessageBox::Yes | QMessageBox::No, m_game_list_frame); + + if (vsh_selectable) + { + mb.setCheckBox(new QCheckBox(tr("Include PS3 Interface (XMB, or VSH)"))); + } + + if (mb.exec() != QMessageBox::Yes) + { + return; + } + + include_vsh = mb.checkBox() && mb.checkBox()->isChecked(); } std::set serials; - if (games.empty()) - { - serials.emplace("vsh.self"); - } - for (const auto& game : (games.empty() ? m_game_list_frame->GetGameInfo() : games)) { serials.emplace(game->info.serial); } + if (include_vsh) + { + serials.emplace("vsh.self"); + } + else if (vsh_selectable) + { + // The VSH entry is part of the full game list, so it has to be removed if unwanted + serials.erase("vsh.self"); + } + const usz total = serials.size(); if (total == 0) From 0dd3d6528c51bdc1bed2b06ba547affe9eb5f046 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 18 Aug 2026 17:59:04 +0300 Subject: [PATCH 03/17] rsx/prog: Move in-shader color remap to FP prolog - We need it for some other stuff --- rpcs3/Emu/RSX/Program/GLSLCommon.cpp | 5 +++ .../RSXProg/RSXFragmentPrologue.glsl | 31 +++++++++++++++++++ .../RSXFragmentTextureDepthConversion.glsl | 29 ----------------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 29f4a29eb..11d374649 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -321,6 +321,11 @@ namespace glsl enabled_options.push_back("_ENABLE_COMPARISON_FUNC"); } + if (props.require_texture_ops && props.require_depth_conversion) + { + enabled_options.push_back("_ENABLE_COLOR_CHANNEL_REMAPPING"); + } + if (props.require_fog_read) { program_common::define_glsl_constants(OS, diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl index 3bf7d121a..7ffd6c0f2 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl @@ -128,4 +128,35 @@ bool comparison_passes(const in float a, const in float b, const in uint func) } #endif +#ifdef _ENABLE_COLOR_CHANNEL_REMAPPING +vec4 remap_vector(const in vec4 color, const in uint remap) +{ + vec4 result; + if (_get_bits(remap, 0, 8) == 0xE4) + { + result = color; + } + else + { + uvec4 remap_channel = uvec4(remap) >> uvec4(2, 4, 6, 0); + remap_channel &= 3; + remap_channel = (remap_channel + 3) % 4; // Map A-R-G-B to R-G-B-A + + // Generate remapped result + result.a = color[remap_channel.a]; + result.r = color[remap_channel.r]; + result.g = color[remap_channel.g]; + result.b = color[remap_channel.b]; + } + + if (_get_bits(remap, 8, 8) == 0xAA) + return result; + + uvec4 remap_select = uvec4(remap) >> uvec4(10, 12, 14, 8); + remap_select &= 3; + bvec4 choice = lessThan(remap_select, uvec4(2)); + return _select(result, vec4(remap_select), choice); +} +#endif + )" diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl index a9737e97f..46e7a9db7 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl @@ -26,35 +26,6 @@ vec4 decode_depth24(const in float depth_value, const in bool depth_float) return color / scale; } -vec4 remap_vector(const in vec4 color, const in uint remap) -{ - vec4 result; - if (_get_bits(remap, 0, 8) == 0xE4) - { - result = color; - } - else - { - uvec4 remap_channel = uvec4(remap) >> uvec4(2, 4, 6, 0); - remap_channel &= 3; - remap_channel = (remap_channel + 3) % 4; // Map A-R-G-B to R-G-B-A - - // Generate remapped result - result.a = color[remap_channel.a]; - result.r = color[remap_channel.r]; - result.g = color[remap_channel.g]; - result.b = color[remap_channel.b]; - } - - if (_get_bits(remap, 8, 8) == 0xAA) - return result; - - uvec4 remap_select = uvec4(remap) >> uvec4(10, 12, 14, 8); - remap_select &= 3; - bvec4 choice = lessThan(remap_select, uvec4(2)); - return _select(result, vec4(remap_select), choice); -} - vec4 convert_z24x8_to_rgba8(const in vec2 depth_stencil, const in uint remap, const in uint flags) { vec4 result = decode_depth24(depth_stencil.x, _test_bit(flags, DEPTH_FLOAT)); From 84ceef9717757c07c5018e38fd8ac329453e99d2 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 18 Aug 2026 18:25:43 +0300 Subject: [PATCH 04/17] rsx/glsl: Shrink command space in ROP_CONTROL structure to make room --- rpcs3/Emu/RSX/Program/GLSLCommon.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.h b/rpcs3/Emu/RSX/Program/GLSLCommon.h index 47fd73058..a9d504614 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.h @@ -19,21 +19,23 @@ namespace rsx POLYGON_STIPPLE_ENABLE_BIT = 3, // Auxilliary config - INT_FRAMEBUFFER_BIT = 16, - MSAA_WRITE_ENABLE_BIT = 17, - FRAG_DEPTH_24_BIT = 18, - FRAG_DEPTH_FLOAT_BIT = 19, + INT_FRAMEBUFFER_BIT = 8, + MSAA_WRITE_ENABLE_BIT = 9, + FRAG_DEPTH_24_BIT = 10, + FRAG_DEPTH_FLOAT_BIT = 11, // Data - ALPHA_FUNC_OFFSET = 20, - MSAA_SAMPLE_CTRL_OFFSET = 23, + ALPHA_FUNC_OFFSET = 12, + MSAA_SAMPLE_CTRL_OFFSET = 15, + MRT_CHANNEL_REMAP_OFFSET = 17, // Data lengths ALPHA_FUNC_NUM_BITS = 3, MSAA_SAMPLE_CTRL_NUM_BITS = 2, + MRT_CHANNEL_REMAP_NUM_BITS = 2, // Meta - ROP_CMD_MASK = 0xF // Commands are encoded in the lower 16 bits + ROP_CMD_MASK = 0xF // Commands are encoded in the lower 4 bits }; struct ROP_control_t From 57ecb4243393ceae620cc2543b725cb0bc6dc3ee Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 18 Aug 2026 18:59:37 +0300 Subject: [PATCH 05/17] rsx/fp: Plumb through support for channel remapping during ROP --- rpcs3/Emu/RSX/Program/GLSLCommon.cpp | 20 ++++++++++++++++++- rpcs3/Emu/RSX/Program/GLSLCommon.h | 8 ++++++++ .../RSXProg/RSXFragmentPrologue.glsl | 19 ++++++++++++++++++ .../GLSLSnippets/RSXProg/RSXROPEpilogue.glsl | 8 ++++++++ rpcs3/Emu/RSX/Program/GLSLTypes.h | 1 + 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 11d374649..8699f3a9f 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -202,9 +202,22 @@ namespace glsl { "MSAA_SAMPLE_CTRL_LENGTH ", rsx::ROP_control_bits::MSAA_SAMPLE_CTRL_NUM_BITS }, { "FRAG_DEPTH_24_BIT ", rsx::ROP_control_bits::FRAG_DEPTH_24_BIT }, { "FRAG_DEPTH_FLOAT_BIT ", rsx::ROP_control_bits::FRAG_DEPTH_FLOAT_BIT }, + { "MRT_CHANNEL_REMAP_OFFSET ", rsx::ROP_control_bits::MRT_CHANNEL_REMAP_OFFSET }, + { "MRT_CHANNEL_REMAP_LENGTH ", rsx::ROP_control_bits::MRT_CHANNEL_REMAP_NUM_BITS }, { "ROP_CMD_MASK ", rsx::ROP_control_bits::ROP_CMD_MASK } }); + if (props.ROP_channel_remap) + { + program_common::define_glsl_constants(OS, + { + { "ROP_REMAP_SWIZZLE_BBBB", rsx::ROP_channel_remap::BBBB }, + { "ROP_REMAP_SWIZZLE_GBGB", rsx::ROP_channel_remap::GBGB }, + { "ROP_REMAP_SWIZZLE_RGB1", rsx::ROP_channel_remap::RGB1 }, + { "ROP_REMAP_SWIZZLE_RGB0", rsx::ROP_channel_remap::RGB0 } + }); + } + program_common::define_glsl_constants(OS, { { "col0", props.fp32_outputs ? "r0" : "h0" }, @@ -321,11 +334,16 @@ namespace glsl enabled_options.push_back("_ENABLE_COMPARISON_FUNC"); } - if (props.require_texture_ops && props.require_depth_conversion) + if (props.ROP_channel_remap || (props.require_texture_ops && props.require_depth_conversion)) { enabled_options.push_back("_ENABLE_COLOR_CHANNEL_REMAPPING"); } + if (props.ROP_channel_remap) + { + enabled_options.push_back("_ENABLE_ROP_CHANNEL_REMAPPING"); + } + if (props.require_fog_read) { program_common::define_glsl_constants(OS, diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.h b/rpcs3/Emu/RSX/Program/GLSLCommon.h index a9d504614..88257a47d 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.h @@ -10,6 +10,14 @@ struct RSXFragmentProgram; namespace rsx { + enum class ROP_channel_remap : u32 + { + BBBB = 0, + GBGB, + RGB1, + RGB0, + }; + enum ROP_control_bits : u32 { // Commands. These trigger explicit action. diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl index 7ffd6c0f2..ea25b11b4 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl @@ -159,4 +159,23 @@ vec4 remap_vector(const in vec4 color, const in uint remap) } #endif +#ifdef _ENABLE_ROP_CHANNEL_REMAPPING +uint get_ROP_channel_remap() +{ + switch (_get_bits(rop_control, MRT_CHANNEL_REMAP_OFFSET, MRT_CHANNEL_REMAP_LENGTH)) + { + case ROP_REMAP_SWIZZLE_BBBB: // B8 + return 0xAAFFu; + case ROP_REMAP_SWIZZLE_GBGB: // G8B8 + return 0xAABBu; + case ROP_REMAP_SWIZZLE_RGB1: // RGB1 + return 0xA9E4u; + case ROP_REMAP_SWIZZLE_RGB0: // RGB0 + return 0xA8E4u; + default: + return 0xAAE4u; + } +} +#endif + )" diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl index af2df8115..0f3376a22 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl @@ -76,6 +76,14 @@ R"( } #endif +#ifdef _ENABLE_ROP_CHANNEL_REMAPPING + const uint ROP_remap = get_ROP_channel_remap(); + col0 = _mrt_color_t(remap_vector(col0, ROP_remap)); + col1 = _mrt_color_t(remap_vector(col1, ROP_remap)); + col2 = _mrt_color_t(remap_vector(col2, ROP_remap)); + col3 = _mrt_color_t(remap_vector(col3, ROP_remap)); +#endif + // Commit ocol0 = col0; ocol1 = col1; diff --git a/rpcs3/Emu/RSX/Program/GLSLTypes.h b/rpcs3/Emu/RSX/Program/GLSLTypes.h index a470ad5e3..7b8f10d13 100644 --- a/rpcs3/Emu/RSX/Program/GLSLTypes.h +++ b/rpcs3/Emu/RSX/Program/GLSLTypes.h @@ -50,6 +50,7 @@ namespace glsl bool ROP_alpha_to_coverage_test : 1; bool ROP_polygon_stipple_test : 1; bool ROP_discard : 1; + bool ROP_channel_remap : 1; // Texturing spec bool require_texture_ops : 1; // Global switch to enable/disable all texture code From 323e2d35a2abd1b6ecbd0054885acbe61404c677 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 18 Aug 2026 20:42:13 +0300 Subject: [PATCH 06/17] rsx/fp: Implement support for ROP_OUTPUT_REMAP in the backends --- rpcs3/Emu/RSX/Common/TextureUtils.cpp | 23 ++++++++++++++++++ rpcs3/Emu/RSX/Common/TextureUtils.h | 2 ++ rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp | 8 ++++++- rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp | 1 + rpcs3/Emu/RSX/NV47/HW/nv4097.cpp | 18 ++++++++++++-- rpcs3/Emu/RSX/Program/GLSLCommon.cpp | 4 +++- rpcs3/Emu/RSX/Program/GLSLCommon.h | 12 +++------- .../RSXProg/RSXFragmentPrologue.glsl | 24 ++++++++++--------- .../GLSLSnippets/RSXProg/RSXROPEpilogue.glsl | 16 ++++++------- rpcs3/Emu/RSX/RSXThread.cpp | 4 ++++ rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp | 4 +++- rpcs3/Emu/RSX/color_utils.h | 13 ++++++++-- rpcs3/Emu/RSX/gcm_enums.h | 2 ++ rpcs3/Emu/RSX/rsx_decode.h | 14 +++++++++++ rpcs3/Emu/RSX/rsx_methods.h | 5 ++++ 15 files changed, 115 insertions(+), 35 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index 3fd0ed68c..28c265bf1 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -3,6 +3,8 @@ #include "TextureUtils.h" #include "../RSXThread.h" #include "../rsx_utils.h" +#include "../color_utils.h" + #include "3rdparty/bcdec/bcdec.hpp" #include "util/asm.hpp" @@ -1870,4 +1872,25 @@ namespace rsx return false; } } + + u32 get_ROP_output_shuffle_index(rsx::surface_color_format format) + { + switch (format) + { + case surface_color_format::b8: + return static_cast(ROP_channel_remap::BBBB); + case surface_color_format::g8b8: + return static_cast(ROP_channel_remap::GBGB); + case surface_color_format::x1r5g5b5_z1r5g5b5: + case surface_color_format::x8r8g8b8_z8r8g8b8: + case surface_color_format::x8b8g8r8_z8b8g8r8: + return static_cast(ROP_channel_remap::RGB0); + case surface_color_format::x1r5g5b5_o1r5g5b5: + case surface_color_format::x8r8g8b8_o8r8g8b8: + case surface_color_format::x8b8g8r8_o8b8g8r8: + return static_cast(ROP_channel_remap::RGB1); + default: + return static_cast(ROP_channel_remap::RGBA); + } + } } diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index 526b91816..7d9aeea87 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -422,4 +422,6 @@ namespace rsx { return is_border_clamped_texture(tex.wrap_s(), tex.wrap_t(), tex.wrap_r(), tex.dimension()); } + + u32 get_ROP_output_shuffle_index(rsx::surface_color_format format); } diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 8ef45e1f0..ba4d1a297 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -662,7 +662,7 @@ namespace rsx } } - void draw_command_processor::fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& /*fragment_program*/) const + void draw_command_processor::fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& fragment_program) const { #pragma pack(push, 1) struct fragment_context_t @@ -684,6 +684,12 @@ namespace rsx const u32 alpha_func = static_cast(REGS(m_ctx)->alpha_func()); rop_control.set_alpha_test_func(alpha_func); + if (fragment_program.ctrl & RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP) + { + const u32 remap_index = get_ROP_output_shuffle_index(REGS(m_ctx)->surface_color()); + rop_control.set_output_remap(remap_index); + } + // Generate wpos coefficients // wpos equation is now as follows (ignoring pixel center offset): // wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0) diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index 3994b2036..fcc286030 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -240,6 +240,7 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.ROP_alpha_to_coverage_test = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE); m_shader_props.ROP_polygon_stipple_test = !!(m_prog.ctrl & RSX_SHADER_CONTROL_POLYGON_STIPPLE); m_shader_props.ROP_discard = !!(m_prog.ctrl & RSX_SHADER_CONTROL_USES_KIL); + m_shader_props.ROP_channel_remap = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP); m_shader_props.require_tex1D_ops = properties.has_tex1D; m_shader_props.require_tex2D_ops = properties.has_tex2D; diff --git a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp index cf7474177..204f44b7c 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp @@ -250,12 +250,26 @@ namespace rsx const auto current = REGS(ctx)->decode(arg); const auto previous = REGS(ctx)->decode(REGS(ctx)->latch); - if (current.is_integer_color_format() != previous.is_integer_color_format()) // Different ROP emulation + // Check for different ROP emulation + if (current.is_integer_color_format() != previous.is_integer_color_format()) { RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_state_dirty; } - if (*current.antialias() != *previous.antialias()) // Antialias control has changed, update ROP parameters + // If swizzle remap changed, we have to flag both the shader and the ROP parameters + if (current.is_remapped_format() != previous.is_remapped_format()) + { + RSX(ctx)->m_graphics_state |= + rsx::pipeline_state::fragment_program_state_dirty | + rsx::pipeline_state::fragment_state_dirty; + } + // If we're still remapping outputs but the format changed, reload ROP params + else if ((current.is_remapped_format() && *current.color_fmt() != *previous.color_fmt())) + { + RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_state_dirty; + } + // If antialias control has changed, also update ROP parameters + else if (*current.antialias() != *previous.antialias()) { RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_state_dirty; } diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 8699f3a9f..d1ce2164a 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -3,6 +3,7 @@ #include "GLSLCommon.h" #include "RSXFragmentProgram.h" +#include "Emu/RSX/color_utils.h" #include "Emu/RSX/gcm_enums.h" #include "Utilities/StrFmt.h" @@ -211,6 +212,7 @@ namespace glsl { program_common::define_glsl_constants(OS, { + { "ROP_REMAP_SWIZZLE_RGBA", rsx::ROP_channel_remap::RGBA }, { "ROP_REMAP_SWIZZLE_BBBB", rsx::ROP_channel_remap::BBBB }, { "ROP_REMAP_SWIZZLE_GBGB", rsx::ROP_channel_remap::GBGB }, { "ROP_REMAP_SWIZZLE_RGB1", rsx::ROP_channel_remap::RGB1 }, @@ -334,7 +336,7 @@ namespace glsl enabled_options.push_back("_ENABLE_COMPARISON_FUNC"); } - if (props.ROP_channel_remap || (props.require_texture_ops && props.require_depth_conversion)) + if (props.require_texture_ops && props.require_depth_conversion) { enabled_options.push_back("_ENABLE_COLOR_CHANNEL_REMAPPING"); } diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.h b/rpcs3/Emu/RSX/Program/GLSLCommon.h index 88257a47d..b4273c4af 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.h @@ -10,14 +10,6 @@ struct RSXFragmentProgram; namespace rsx { - enum class ROP_channel_remap : u32 - { - BBBB = 0, - GBGB, - RGB1, - RGB0, - }; - enum ROP_control_bits : u32 { // Commands. These trigger explicit action. @@ -40,7 +32,7 @@ namespace rsx // Data lengths ALPHA_FUNC_NUM_BITS = 3, MSAA_SAMPLE_CTRL_NUM_BITS = 2, - MRT_CHANNEL_REMAP_NUM_BITS = 2, + MRT_CHANNEL_REMAP_NUM_BITS = 3, // Meta ROP_CMD_MASK = 0xF // Commands are encoded in the lower 4 bits @@ -60,6 +52,8 @@ namespace rsx void set_alpha_test_func(uint func) { value |= (func << ROP_control_bits::ALPHA_FUNC_OFFSET); } void set_msaa_control(uint ctrl) { value |= (ctrl << ROP_control_bits::MSAA_SAMPLE_CTRL_OFFSET); } + + void set_output_remap(uint remap) { value |= (remap << ROP_control_bits::MRT_CHANNEL_REMAP_OFFSET); } }; } diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl index ea25b11b4..a75ea84d6 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl @@ -160,20 +160,22 @@ vec4 remap_vector(const in vec4 color, const in uint remap) #endif #ifdef _ENABLE_ROP_CHANNEL_REMAPPING -uint get_ROP_channel_remap() +#define get_ROP_channel_remap() _get_bits(rop_control, MRT_CHANNEL_REMAP_OFFSET, MRT_CHANNEL_REMAP_LENGTH) +vec4 remap_ROP_output(const in vec4 col, const in uint remap_index) { - switch (_get_bits(rop_control, MRT_CHANNEL_REMAP_OFFSET, MRT_CHANNEL_REMAP_LENGTH)) + switch (remap_index) { - case ROP_REMAP_SWIZZLE_BBBB: // B8 - return 0xAAFFu; - case ROP_REMAP_SWIZZLE_GBGB: // G8B8 - return 0xAABBu; - case ROP_REMAP_SWIZZLE_RGB1: // RGB1 - return 0xA9E4u; - case ROP_REMAP_SWIZZLE_RGB0: // RGB0 - return 0xA8E4u; default: - return 0xAAE4u; + case ROP_REMAP_SWIZZLE_RGBA: // RGBA + return col; + case ROP_REMAP_SWIZZLE_BBBB: // B8 + return col.bbbb; + case ROP_REMAP_SWIZZLE_GBGB: // G8B8 + return col.bgbg; + case ROP_REMAP_SWIZZLE_RGB1: // RGB1 + return vec4(col.rgb, 1.f); + case ROP_REMAP_SWIZZLE_RGB0: // RGB0 + return vec4(col.rgb, 0.f); } } #endif diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl index 0f3376a22..a9229fc6f 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXROPEpilogue.glsl @@ -58,6 +58,14 @@ R"( } #endif +#ifdef _ENABLE_ROP_CHANNEL_REMAPPING + const uint ROP_remap = get_ROP_channel_remap(); + col0 = _mrt_color_t(remap_ROP_output(col0, ROP_remap)); + col1 = _mrt_color_t(remap_ROP_output(col1, ROP_remap)); + col2 = _mrt_color_t(remap_ROP_output(col2, ROP_remap)); + col3 = _mrt_color_t(remap_ROP_output(col3, ROP_remap)); +#endif + #ifdef _ENABLE_PROGRAMMABLE_BLENDING switch (framebufferCount) { @@ -76,14 +84,6 @@ R"( } #endif -#ifdef _ENABLE_ROP_CHANNEL_REMAPPING - const uint ROP_remap = get_ROP_channel_remap(); - col0 = _mrt_color_t(remap_vector(col0, ROP_remap)); - col1 = _mrt_color_t(remap_vector(col1, ROP_remap)); - col2 = _mrt_color_t(remap_vector(col2, ROP_remap)); - col3 = _mrt_color_t(remap_vector(col3, ROP_remap)); -#endif - // Commit ocol0 = col0; ocol1 = col1; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 3bb3fb42a..20b3be41f 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -2221,6 +2221,10 @@ namespace rsx { current_fragment_program.ctrl |= RSX_SHADER_CONTROL_SRGB_FRAMEBUFFER; } + if (m_ctx->register_state->surface_is_swizzle_remapped()) + { + current_fragment_program.ctrl |= RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP; + } break; } diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index a8bd23e34..ffd4842fe 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -356,6 +356,7 @@ void VKFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.ROP_alpha_to_coverage_test = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE); m_shader_props.ROP_polygon_stipple_test = !!(m_prog.ctrl & RSX_SHADER_CONTROL_POLYGON_STIPPLE); m_shader_props.ROP_discard = !!(m_prog.ctrl & RSX_SHADER_CONTROL_USES_KIL); + m_shader_props.ROP_channel_remap = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP); m_shader_props.require_tex1D_ops = properties.has_tex1D; m_shader_props.require_tex2D_ops = properties.has_tex2D; @@ -490,7 +491,8 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) OS << "{\n"; if ((m_prog.ctrl & RSX_SHADER_CONTROL_ALPHA_TEST) || - (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE)) + (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE) || + (m_prog.ctrl & RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP)) { OS << " const uint rop_control = fs_contexts[_fs_context_offset].rop_control;\n" diff --git a/rpcs3/Emu/RSX/color_utils.h b/rpcs3/Emu/RSX/color_utils.h index 25aa6eb7e..9a3717178 100644 --- a/rpcs3/Emu/RSX/color_utils.h +++ b/rpcs3/Emu/RSX/color_utils.h @@ -53,6 +53,15 @@ namespace rsx FORMAT_FEATURES_OFFSET = FF_SIGNED_BIT, }; + enum class ROP_channel_remap : u32 + { + RGBA = 0, + BBBB, + GBGB, + RGB1, + RGB0, + }; + struct texture_channel_remap_t { u32 encoded = 0xDEAD; @@ -301,14 +310,14 @@ namespace rsx case rsx::surface_color_format::a8b8g8r8: case rsx::surface_color_format::w16z16y16x16: case rsx::surface_color_format::w32z32y32x32: - return rgba; case rsx::surface_color_format::x1r5g5b5_z1r5g5b5: case rsx::surface_color_format::x1r5g5b5_o1r5g5b5: - case rsx::surface_color_format::r5g6b5: case rsx::surface_color_format::x8r8g8b8_z8r8g8b8: case rsx::surface_color_format::x8r8g8b8_o8r8g8b8: case rsx::surface_color_format::x8b8g8r8_z8b8g8r8: case rsx::surface_color_format::x8b8g8r8_o8b8g8r8: + return rgba; + case rsx::surface_color_format::r5g6b5: return rgb; case rsx::surface_color_format::g8b8: return rg; diff --git a/rpcs3/Emu/RSX/gcm_enums.h b/rpcs3/Emu/RSX/gcm_enums.h index aaae09768..727f0b153 100644 --- a/rpcs3/Emu/RSX/gcm_enums.h +++ b/rpcs3/Emu/RSX/gcm_enums.h @@ -473,6 +473,8 @@ namespace gcm RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE = 0x08000000, // Emulate depth comparisons RSX_SHADER_CONTROL_MULTISAMPLED_ZBUFFER = 0x10000000, // Z buffer is multisampled. Only affects depth comparison behavior at this time. + RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP = 0x20000000, // ROP outputs need channel swizzles. + // Meta RSX_SHADER_CONTROL_META_USES_DISCARD = (RSX_SHADER_CONTROL_USES_KIL | RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL | RSX_SHADER_CONTROL_ALPHA_TEST | RSX_SHADER_CONTROL_POLYGON_STIPPLE | RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE) }; diff --git a/rpcs3/Emu/RSX/rsx_decode.h b/rpcs3/Emu/RSX/rsx_decode.h index cbd36f73d..3571ec194 100644 --- a/rpcs3/Emu/RSX/rsx_decode.h +++ b/rpcs3/Emu/RSX/rsx_decode.h @@ -3369,6 +3369,20 @@ struct registers_decoder { return color_fmt() < surface_color_format::w16z16y16x16; } + + bool is_remapped_format() const + { + static constexpr u32 s_remapped_formats = + (1u << CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5) | + (1u << CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5) | + (1u << CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8) | + (1u << CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8) | + (1u << CELL_GCM_SURFACE_B8) | + (1u << CELL_GCM_SURFACE_G8B8) | + (1u << CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8) | + (1u << CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8); + return !!((1u << color_fmt_raw()) & s_remapped_formats); + } }; static void dump(std::string& out, const decoded_type& decoded) diff --git a/rpcs3/Emu/RSX/rsx_methods.h b/rpcs3/Emu/RSX/rsx_methods.h index 8e1dd698f..9fee93022 100644 --- a/rpcs3/Emu/RSX/rsx_methods.h +++ b/rpcs3/Emu/RSX/rsx_methods.h @@ -935,6 +935,11 @@ namespace rsx return decode().log2width(); } + bool surface_is_swizzle_remapped() const + { + return decode().is_remapped_format(); + } + u32 vertex_data_base_offset() const { return decode().vertex_data_base_offset(); From ca25fbaa5df9a35815a258e53d4899f36e22578d Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 18 Aug 2026 22:39:06 +0300 Subject: [PATCH 07/17] vk/gl: Support ROP output remap in the interpreter --- rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp | 1 + .../RSX/Program/GLSLInterpreter/FragmentInterpreter.glsl | 8 ++++++++ rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp | 1 + 3 files changed, 10 insertions(+) diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index 7f1d1b97e..2e155381b 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -412,6 +412,7 @@ namespace gl { .domain = ::glsl::program_domain::glsl_fragment_program, .require_lit_emulation = true, + .ROP_channel_remap = true, }; ::glsl::insert_glsl_legacy_function(builder, properties); diff --git a/rpcs3/Emu/RSX/Program/GLSLInterpreter/FragmentInterpreter.glsl b/rpcs3/Emu/RSX/Program/GLSLInterpreter/FragmentInterpreter.glsl index 5f72789c5..075cac01e 100644 --- a/rpcs3/Emu/RSX/Program/GLSLInterpreter/FragmentInterpreter.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLInterpreter/FragmentInterpreter.glsl @@ -888,6 +888,14 @@ void main() #ifdef ALPHA_TEST_NEQUAL if (ocol0.a == alpha_ref) discard; // nequal #endif + +#ifdef _ENABLE_ROP_CHANNEL_REMAPPING + const uint ROP_remap = get_ROP_channel_remap(); + ocol0 = remap_ROP_output(ocol0, ROP_remap); + ocol1 = remap_ROP_output(ocol1, ROP_remap); + ocol2 = remap_ROP_output(ocol2, ROP_remap); + ocol3 = remap_ROP_output(ocol3, ROP_remap); +#endif } )" diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp index e530b74b4..ec16f4b8c 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp @@ -289,6 +289,7 @@ namespace vk { .domain = ::glsl::program_domain::glsl_fragment_program, .require_lit_emulation = true, + .ROP_channel_remap = true, }; u32 len; From b78bae0b9f63e8c0f33b37ccaa6036abb02f51c8 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 18 Aug 2026 22:58:17 +0300 Subject: [PATCH 08/17] rsx: Integrate ROP remapping to the interpreter system - Base pipelines have the remapping active, optimized variants can toggle it away. --- rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp | 3 ++- rpcs3/Emu/RSX/Program/ShaderInterpreter.h | 23 ++++++++++++----------- rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index 2e155381b..f52b7462f 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -145,6 +145,7 @@ namespace gl if (fp_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) opt |= COMPILER_OPT_ENABLE_DEPTH_EXPORT; if (fp_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) opt |= COMPILER_OPT_ENABLE_F32_EXPORT; if (fp_ctrl & RSX_SHADER_CONTROL_USES_KIL) opt |= COMPILER_OPT_ENABLE_KIL; + if (fp_ctrl & RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP) opt |= COMPILER_OPT_ENABLE_ROP_REMAP; if (metadata.referenced_textures_mask) opt |= COMPILER_OPT_ENABLE_TEXTURES; if (metadata.has_branch_instructions) opt |= COMPILER_OPT_ENABLE_FLOW_CTRL; if (metadata.has_pack_instructions) opt |= COMPILER_OPT_ENABLE_PACKING; @@ -412,7 +413,7 @@ namespace gl { .domain = ::glsl::program_domain::glsl_fragment_program, .require_lit_emulation = true, - .ROP_channel_remap = true, + .ROP_channel_remap = !!(compiler_options & COMPILER_OPT_ENABLE_ROP_REMAP), }; ::glsl::insert_glsl_legacy_function(builder, properties); diff --git a/rpcs3/Emu/RSX/Program/ShaderInterpreter.h b/rpcs3/Emu/RSX/Program/ShaderInterpreter.h index d08b6f00a..c957dfad3 100644 --- a/rpcs3/Emu/RSX/Program/ShaderInterpreter.h +++ b/rpcs3/Emu/RSX/Program/ShaderInterpreter.h @@ -16,30 +16,31 @@ namespace program_common COMPILER_OPT_ENABLE_KIL = (1 << 4), COMPILER_OPT_ENABLE_STIPPLING = (1 << 5), COMPILER_OPT_ENABLE_FLOW_CTRL = (1 << 6), + COMPILER_OPT_ENABLE_ROP_REMAP = (1 << 7), // VS Mix-N-Match - COMPILER_OPT_ENABLE_INSTANCING = (1 << 7), - COMPILER_OPT_ENABLE_VTX_TEXTURES = (1 << 8), + COMPILER_OPT_ENABLE_INSTANCING = (1 << 8), + COMPILER_OPT_ENABLE_VTX_TEXTURES = (1 << 9), // Exclusive bits. Only one can be set at a time - COMPILER_OPT_ENABLE_ALPHA_TEST_GE = (1 << 9), - COMPILER_OPT_ENABLE_ALPHA_TEST_G = (1 << 10), - COMPILER_OPT_ENABLE_ALPHA_TEST_LE = (1 << 11), - COMPILER_OPT_ENABLE_ALPHA_TEST_L = (1 << 12), - COMPILER_OPT_ENABLE_ALPHA_TEST_EQ = (1 << 13), - COMPILER_OPT_ENABLE_ALPHA_TEST_NE = (1 << 14), + COMPILER_OPT_ENABLE_ALPHA_TEST_GE = (1 << 10), + COMPILER_OPT_ENABLE_ALPHA_TEST_G = (1 << 11), + COMPILER_OPT_ENABLE_ALPHA_TEST_LE = (1 << 12), + COMPILER_OPT_ENABLE_ALPHA_TEST_L = (1 << 13), + COMPILER_OPT_ENABLE_ALPHA_TEST_EQ = (1 << 14), + COMPILER_OPT_ENABLE_ALPHA_TEST_NE = (1 << 15), // Meta COMPILER_OPT_MAX = COMPILER_OPT_ENABLE_ALPHA_TEST_NE, - COMPILER_OPT_ALPHA_TEST_MASK = (0b111111 << 9), + COMPILER_OPT_ALPHA_TEST_MASK = (0b111111 << 10), COMPILER_OPT_ALL_VS_MASK = COMPILER_OPT_ENABLE_INSTANCING | COMPILER_OPT_ENABLE_VTX_TEXTURES, - COMPILER_OPT_BASE_FS_MASK = 0b1111111, + COMPILER_OPT_BASE_FS_MASK = 0b11111111, COMPILER_OPT_ALL_FS_MASK = COMPILER_OPT_BASE_FS_MASK | COMPILER_OPT_ALPHA_TEST_MASK, COMPILER_OPT_VS_EXCL_MASK = COMPILER_OPT_ENABLE_INSTANCING, COMPILER_OPT_FS_EXCL_MASK = COMPILER_OPT_ALPHA_TEST_MASK | COMPILER_OPT_ENABLE_STIPPLING | COMPILER_OPT_ENABLE_DEPTH_EXPORT | COMPILER_OPT_ENABLE_F32_EXPORT, // Bounds - COMPILER_OPT_FS_MAX = COMPILER_OPT_ENABLE_FLOW_CTRL, + COMPILER_OPT_FS_MAX = COMPILER_OPT_ENABLE_ROP_REMAP, COMPILER_OPT_FS_MIN = COMPILER_OPT_ENABLE_TEXTURES, COMPILER_OPT_VS_MAX = COMPILER_OPT_ENABLE_VTX_TEXTURES, COMPILER_OPT_VS_MIN = COMPILER_OPT_ENABLE_INSTANCING, diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp index ec16f4b8c..7c8adea22 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp @@ -289,7 +289,7 @@ namespace vk { .domain = ::glsl::program_domain::glsl_fragment_program, .require_lit_emulation = true, - .ROP_channel_remap = true, + .ROP_channel_remap = !!(compiler_options & COMPILER_OPT_ENABLE_ROP_REMAP), }; u32 len; @@ -591,6 +591,7 @@ namespace vk if (fp_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) key.compiler_opt |= COMPILER_OPT_ENABLE_DEPTH_EXPORT; if (fp_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) key.compiler_opt |= COMPILER_OPT_ENABLE_F32_EXPORT; if (fp_ctrl & RSX_SHADER_CONTROL_USES_KIL) key.compiler_opt |= COMPILER_OPT_ENABLE_KIL; + if (fp_ctrl & RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP) key.compiler_opt |= COMPILER_OPT_ENABLE_ROP_REMAP; if (fp_metadata.referenced_textures_mask) key.compiler_opt |= COMPILER_OPT_ENABLE_TEXTURES; if (fp_metadata.has_branch_instructions) key.compiler_opt |= COMPILER_OPT_ENABLE_FLOW_CTRL; if (fp_metadata.has_pack_instructions) key.compiler_opt |= COMPILER_OPT_ENABLE_PACKING; From 64da425e8107918edcd22c5ecdf575822efca76c Mon Sep 17 00:00:00 2001 From: kd-11 Date: Wed, 19 Aug 2026 20:03:27 +0300 Subject: [PATCH 09/17] gl: Fix copy_image_static behavior when formats are mismatched --- rpcs3/Emu/RSX/GL/GLTextureCache.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index 5219c104d..4cef3b72e 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -578,8 +578,13 @@ namespace gl { ensure(desc.sections_to_copy.size() == 1); const auto& section = desc.sections_to_copy.front(); - return create_temporary_subresource_impl(cmd, section.src, static_cast(section.src->get_internal_format()), - GL_TEXTURE_2D, desc.gcm_format, desc.width, desc.height, 1, 1, desc.remap, §ion); + return create_temporary_subresource_impl( + cmd, section.src, + GL_NONE, // NOTE: Do not force this to section.get_sized_internal_fmt(). Leave it as GL_NONE, let the callee find the right type in case of bitcast. + GL_TEXTURE_2D, desc.gcm_format, + desc.width, desc.height, 1, 1, + desc.remap, + §ion); } gl::texture_view* generate_cubemap_from_images(gl::command_context& cmd, const deferred_subresource& desc) override From d12d8782af5e13c800dd060537fb87d126b9ab57 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 19 Aug 2026 20:19:44 +0200 Subject: [PATCH 10/17] unpkg: Mark pkg installation as failed if any thread throws an exception --- rpcs3/Crypto/unpkg.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index 997ae6b99..80c74197c 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -1359,14 +1359,32 @@ package_install_result package_reader::extract_data(std::deque& if (reader.m_num_failures == 0) { const usz thread_count = std::min(utils::get_thread_count(), reader.m_install_entries.size()); + atomic_t num_threads_succeeded {0}; // Check if any thread didn't finish. For example when hitting an exception. - named_thread_group workers("PKG Installer "sv, std::max(::narrow(thread_count), 1) - 1, [&]() + if (thread_count > 1) + { + named_thread_group workers("PKG Installer "sv, ::narrow(thread_count) - 1, [&]() + { + reader.extract_worker(); + num_threads_succeeded++; + }); + + reader.extract_worker(); + num_threads_succeeded++; + + workers.join(); + } + else { reader.extract_worker(); - }); + num_threads_succeeded++; + } - reader.extract_worker(); - workers.join(); + if (thread_count != num_threads_succeeded) + { + pkg_log.error("%d thread(s) failed with an exception!", thread_count - num_threads_succeeded); + reader.m_num_failures++; + } } num_failures += reader.m_num_failures; From c122edd6383901c10483511ac1b1960888e217ac Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 19 Aug 2026 20:35:31 +0200 Subject: [PATCH 11/17] unpkg: Fix buffer size checks --- rpcs3/Crypto/unpkg.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index 80c74197c..da503bc85 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -1216,10 +1216,14 @@ void package_reader::extract_worker() while (read_size < size) { const u64 block_size = std::min(BUF_SIZE, size - read_size); - const u64 available_buffer_size = buffer.size() - read_size; + u64 available_buffer_size = original_size - read_size; + + if (buffer.data() == ptr) + { + available_buffer_size = buffer.size() - read_size; + ensure(buffer.size() == original_size + BUF_PADDING); + } - ensure(buffer.data() == ptr); - ensure(buffer.size() == original_size + BUF_PADDING); ensure(available_buffer_size >= block_size); const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), std::span{static_cast(ptr) + read_size, available_buffer_size}); From e5e280cd3f669b01da3e866e06ecd491c8eb6daf Mon Sep 17 00:00:00 2001 From: digant73 Date: Tue, 18 Aug 2026 20:36:27 +0200 Subject: [PATCH 12/17] Fix duplicate path addition --- rpcs3/rpcs3qt/main_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 65c7f0da2..ca4e3a3ab 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -4180,7 +4180,7 @@ void main_window::AddGamesFromDirs(QStringList&& paths) { for (const auto& dir_path : paths) { - if (dir_path.startsWith(game->info.path.c_str()) && fs::exists(game->info.path)) + if (Emu.IsPathInsideDir(game->info.path, dir_path.toStdString())) { existing.insert(game->info.path); break; From 5d2601599e9ecfebdbff7d176d075871a4e17c1c Mon Sep 17 00:00:00 2001 From: yahfz <17715080+Yahfz@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:26:55 -0300 Subject: [PATCH 13/17] [SPU LLVM] Optimize AVX-512 XFloat conversion --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 06a1aa1d1..97c6381f8 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -716,6 +716,16 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator ensure(val && val->getType() == get_type()); const auto x = m_ir->CreateZExt(val, get_type()); + + if (m_use_avx512) + { + const auto s = m_ir->CreateAnd(m_ir->CreateShl(x, 32), 0x8000000000000000); + const auto m = m_ir->CreateAnd(m_ir->CreateShl(x, 29), 0x0fffffffe0000000); + const auto f = m_ir->CreateAdd(m_ir->CreateOr(s, m), splat(0x3800000000000000).eval(m_ir)); + const auto e = m_ir->CreateAnd(val, 0x7f800000); + return uint64_as_double(m_ir->CreateSelect(m_ir->CreateIsNotNull(e), f, s)); + } + const auto s = m_ir->CreateShl(m_ir->CreateAnd(x, 0x80000000), 32); const auto a = m_ir->CreateAnd(x, 0x7fffffff); const auto m = m_ir->CreateShl(m_ir->CreateAdd(a, splat(0x1c0000000).eval(m_ir)), 29); @@ -729,6 +739,18 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator { ensure(val && val->getType() == get_type()); + if (m_use_avx512) + { + const auto d = double_as_uint64(val); + const auto smax = splat(0x47ffffffe0000000).eval(m_ir); + const auto smin = splat(0x3810000000000000).eval(m_ir); + const auto a = m_ir->CreateAnd(d, 0x7fffffffe0000000); + const auto n = m_ir->CreateICmpUGE(a, smin); + const auto c = m_ir->CreateSelect(m_ir->CreateICmpULT(a, smax), a, smax); + const auto r = m_ir->CreateOr(c, m_ir->CreateAnd(d, 0x8000000000000000)); + return uint64_as_double(m_ir->CreateSelect(n, r, splat(0).eval(m_ir))); + } + const auto smax = uint64_as_double(splat(0x47ffffffe0000000).eval(m_ir)); const auto smin = uint64_as_double(splat(0x3810000000000000).eval(m_ir)); From c4eff69711786de8042f574826504433bad12e9f Mon Sep 17 00:00:00 2001 From: yahfz <17715080+Yahfz@users.noreply.github.com> Date: Thu, 20 Aug 2026 02:48:40 -0300 Subject: [PATCH 14/17] SPU LLVM: Document AVX-512 XFloat lowering --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 97c6381f8..54cde3b62 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -717,6 +717,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto x = m_ir->CreateZExt(val, get_type()); + // Use integer operations here so LLVM can fold the masks into VPTERNLOG if (m_use_avx512) { const auto s = m_ir->CreateAnd(m_ir->CreateShl(x, 32), 0x8000000000000000); @@ -739,6 +740,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator { ensure(val && val->getType() == get_type()); + // Use integer operations here so LLVM can fold the masks into VPTERNLOG if (m_use_avx512) { const auto d = double_as_uint64(val); From 358101c47b2d9336d064e847c7d45f5c35bb52b8 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 20 Aug 2026 01:55:20 +0200 Subject: [PATCH 15/17] Fix unused variable warnings --- rpcs3/Emu/Cell/Modules/sys_libc_.cpp | 2 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 3 --- rpcs3/Emu/Cell/lv2/lv2.cpp | 2 +- rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp | 2 +- rpcs3/Emu/RSX/GL/glutils/blitter.cpp | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sys_libc_.cpp b/rpcs3/Emu/Cell/Modules/sys_libc_.cpp index 7fc2d74d1..787d7a39a 100644 --- a/rpcs3/Emu/Cell/Modules/sys_libc_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_libc_.cpp @@ -408,7 +408,7 @@ std::pair vm::hle_malloc_allocator::alloc(u32 size, u32 align) return { vm::cast(addr), addr ? size : 0 }; } -void vm::hle_malloc_allocator::dealloc(u32 addr, u32 size) noexcept +void vm::hle_malloc_allocator::dealloc(u32 addr, u32 /*size*/) noexcept { const auto ppu = ensure(cpu_thread::get_current()); ppu_execute<&_sys_free>(*ppu, addr); diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index c8349bcf2..bb3765577 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -40,9 +40,6 @@ bool spu_thread::read_reg(const u32 addr, u32& value) { const u32 offset = addr - (RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index) - RAW_SPU_PROB_OFFSET; - raw_spu_log_stats_t stats{}; - stats.mmio_offset = offset; - const auto [old_stats, is_changed] = mmio_stats.fetch_op([&](raw_spu_log_stats_t& old) { if (old.mmio_offset == offset) diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 488e95f14..676df5ab4 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1053,7 +1053,7 @@ void fmt_class_string::format(std::string& out, u64 arg) if (upper == s_error_codes_formatting_by_type.begin()) { // Format as unknown - format_enum(out, arg, [](auto error) + format_enum(out, arg, [](auto /*error*/) { return unknown; }); diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index f52b7462f..21a35bdb4 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -578,7 +578,7 @@ namespace gl } } - void shader_interpreter::flush_vertex_texture_bindings(glsl::program* program) + void shader_interpreter::flush_vertex_texture_bindings(glsl::program* /*program*/) { // TODO } diff --git a/rpcs3/Emu/RSX/GL/glutils/blitter.cpp b/rpcs3/Emu/RSX/GL/glutils/blitter.cpp index 1beecb731..71a1b63a1 100644 --- a/rpcs3/Emu/RSX/GL/glutils/blitter.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/blitter.cpp @@ -59,7 +59,7 @@ namespace gl } void blitter::copy_image( - gl::command_context& cmd, + gl::command_context& /*cmd*/, const texture* src, const texture* dst, const position3i& src_offset, const position3i& dst_offset, From 8a6c96745abc84707a0185367fe30a4711478d3c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 20 Aug 2026 01:55:36 +0200 Subject: [PATCH 16/17] Fix iso magic read check --- rpcs3/Loader/ISO.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Loader/ISO.cpp b/rpcs3/Loader/ISO.cpp index 609d4e2b4..43d5b9d93 100644 --- a/rpcs3/Loader/ISO.cpp +++ b/rpcs3/Loader/ISO.cpp @@ -65,7 +65,7 @@ static bool is_iso_file(iso_file& file, u64* size = nullptr) char magic[5]; - if (!file.read_at(32768ULL + 1, magic, 5) == 5) + if (file.read_at(32768ULL + 1, magic, 5) != 5) { return false; } From bab81aa23e66ea0014ddcfe506caea640db6569a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 20 Aug 2026 01:57:18 +0200 Subject: [PATCH 17/17] Fix rpcn type cast warnings --- rpcs3/Emu/NP/rpcn_client.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 7de63afc0..31c4cbde5 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -720,7 +720,7 @@ namespace rpcn if (data.size() != 4) return error_and_disconnect("Invalid size of ServerInfo packet"); - received_version = reinterpret_cast&>(data[0]); + received_version = read_from_ptr>(data, 0); server_info_received = true; break; } @@ -1656,7 +1656,7 @@ namespace rpcn { std::vector data(COMMUNICATION_ID_SIZE + sizeof(u16)); rpcn_client::write_communication_id(communication_id, data); - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = server_id; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, server_id); return forge_send(CommandType::GetWorldList, req_id, data); } @@ -2209,7 +2209,7 @@ namespace rpcn pb_req.SerializeToString(&serialized); std::vector data(serialized.size() + sizeof(u32)); - reinterpret_cast&>(data[0]) = static_cast(serialized.size()); + write_to_ptr>(data, 0, static_cast(serialized.size())); memcpy(data.data() + sizeof(u32), serialized.data(), serialized.size()); return forge_send(CommandType::SendMessage, rpcn_request_counter.fetch_add(1), data); @@ -2309,9 +2309,9 @@ namespace rpcn std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize + sizeof(u32) + score_data.size()); rpcn_client::write_communication_id(communication_id, data); - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); + write_to_ptr>(data, COMMUNICATION_ID_SIZE, static_cast(bufsize)); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), serialized.data(), bufsize); - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize]) = static_cast(score_data.size()); + write_to_ptr>(data, COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize, static_cast(score_data.size())); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize + sizeof(u32), score_data.data(), score_data.size()); return forge_send(CommandType::RecordScoreData, req_id, data); @@ -2612,8 +2612,8 @@ namespace rpcn { std::vector data(COMMUNICATION_ID_SIZE + sizeof(s32) + sizeof(s64)); rpcn_client::write_communication_id(communication_id, data); - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = trophy_id; - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE + sizeof(s32)]) = timestamp; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, trophy_id); + write_to_ptr>(data, COMMUNICATION_ID_SIZE + sizeof(s32), timestamp); return forge_send(CommandType::UnlockTrophy, rpcn_request_counter.fetch_add(1), data); } @@ -2626,14 +2626,14 @@ namespace rpcn std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32) + count * (sizeof(s32) + sizeof(s64))), reply_data; rpcn_client::write_communication_id(communication_id, data); - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = count; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, count); usz offset = COMMUNICATION_ID_SIZE + sizeof(u32); for (const auto& [tid, ts] : local_unlocked) { - reinterpret_cast&>(data[offset]) = tid; + write_to_ptr>(data, offset, tid); offset += sizeof(s32); - reinterpret_cast&>(data[offset]) = ts; + write_to_ptr>(data, offset, ts); offset += sizeof(s64); } @@ -2927,7 +2927,7 @@ namespace rpcn rpcn_client::write_communication_id(com_id, data); - reinterpret_cast&>(data[COMMUNICATION_ID_SIZE]) = static_cast(bufsize); + write_to_ptr>(data, COMMUNICATION_ID_SIZE, static_cast(bufsize)); memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), serialized_data.data(), bufsize); return forge_send(command, packet_id, data); @@ -2938,7 +2938,7 @@ namespace rpcn const usz bufsize = serialized_data.size(); std::vector data(sizeof(u32) + bufsize); - reinterpret_cast&>(data[0]) = static_cast(bufsize); + write_to_ptr>(data, 0, static_cast(bufsize)); memcpy(data.data() + sizeof(u32), serialized_data.data(), bufsize); return forge_send(command, packet_id, data); @@ -2950,9 +2950,9 @@ namespace rpcn std::vector packet(packet_size); packet[0] = static_cast(PacketType::Request); - reinterpret_cast&>(packet[1]) = static_cast(command); - reinterpret_cast&>(packet[3]) = ::narrow(packet_size); - reinterpret_cast&>(packet[7]) = packet_id; + write_to_ptr>(packet, 1, static_cast(command)); + write_to_ptr>(packet, 3, ::narrow(packet_size)); + write_to_ptr>(packet, 7, packet_id); memcpy(packet.data() + RPCN_HEADER_SIZE, data.data(), data.size()); return packet;