Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7e87fce
rsx: Update programmable blending code to be in line with current emu…
kd-11 Aug 28, 2026
3dc1f37
rsx/vk: Plumb programmable blending shader emitter
kd-11 Aug 28, 2026
10f4784
vk: Properly propagate programmable blending flag down to renderpass …
kd-11 Aug 28, 2026
e75caa6
vk: Bind input attachments if required by pipeline configuration
kd-11 Aug 28, 2026
162716e
vk: Properly handle sub-pass dependency barriers
kd-11 Aug 28, 2026
2e34301
vk: Set attachment usage flags to allow input attachments always
kd-11 Aug 29, 2026
855b4b4
rsx/vk: Respect programmable blending when requested
kd-11 Aug 29, 2026
86b42c2
rsx: Properly implement signed blending modes
kd-11 Aug 29, 2026
3718885
rsx/fp: Fix failing test cases for programmable blending
kd-11 Sep 2, 2026
6050bfe
rsx: Support variable blend target masks with programmable blending
kd-11 Sep 3, 2026
d0fa8f9
rsx: Minor refactor to prepare for MSAA support
kd-11 Sep 3, 2026
ee9dcd5
rsx: Minor optimization wrt shader export program options
kd-11 Sep 3, 2026
7d473d6
rsx/vk: Implement programmable blending for MSAA targets
kd-11 Sep 3, 2026
778b3f8
vk: Clean up decode_rsx_state in preparation for refactor
kd-11 Sep 4, 2026
5556994
vk/gl: Fix byte order for RGB565 formats in line with hardware tests
kd-11 Sep 4, 2026
c97c458
rsx: Fix ROP operation order.
kd-11 Sep 4, 2026
afc9428
rsx: Fix a really weird quirk when blending with XRGB output formats
kd-11 Sep 4, 2026
cb65b84
rsx: Don't force PB when we don't need to for performance reasons
kd-11 Sep 4, 2026
dc1f9f4
rsx: Fix decoding of 16-bit blend constants
kd-11 Sep 4, 2026
45def4c
rsx/qt: Add a debug setting to force programmable blending
kd-11 Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions rpcs3/Emu/RSX/Common/TextureUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1694,15 +1694,16 @@ namespace rsx

std::pair<u32, bool> get_compatible_gcm_format(rsx::surface_color_format format)
{
// NOTE: HW tests prove that all multibyte formats need to swap bytes
switch (format)
{
case rsx::surface_color_format::r5g6b5:
return{ CELL_GCM_TEXTURE_R5G6B5, false };
return{ CELL_GCM_TEXTURE_R5G6B5, true };

case rsx::surface_color_format::x8r8g8b8_z8r8g8b8:
case rsx::surface_color_format::x8r8g8b8_o8r8g8b8:
case rsx::surface_color_format::a8r8g8b8:
return{ CELL_GCM_TEXTURE_A8R8G8B8, true }; //verified
return{ CELL_GCM_TEXTURE_A8R8G8B8, true };

case rsx::surface_color_format::x8b8g8r8_o8b8g8r8:
case rsx::surface_color_format::x8b8g8r8_z8b8g8r8:
Expand All @@ -1717,7 +1718,7 @@ namespace rsx

case rsx::surface_color_format::x1r5g5b5_o1r5g5b5:
case rsx::surface_color_format::x1r5g5b5_z1r5g5b5:
return{ CELL_GCM_TEXTURE_A1R5G5B5, false };
return{ CELL_GCM_TEXTURE_A1R5G5B5, true };

case rsx::surface_color_format::b8:
return{ CELL_GCM_TEXTURE_B8, false };
Expand All @@ -1726,7 +1727,7 @@ namespace rsx
return{ CELL_GCM_TEXTURE_G8B8, true };

case rsx::surface_color_format::x32:
return{ CELL_GCM_TEXTURE_X32_FLOAT, true }; //verified
return{ CELL_GCM_TEXTURE_X32_FLOAT, true };
default:
fmt::throw_exception("Unhandled surface format 0x%x", static_cast<u32>(format));
}
Expand Down
6 changes: 6 additions & 0 deletions rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,12 @@ namespace rsx
rop_control.set_output_remap(remap_index);
}

if (fragment_program.ctrl & RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING)
{
const auto blend_enable_mask = REGS(m_ctx)->blend_enabled_mask() & REGS(m_ctx)->surface_color_target_mask();
rop_control.set_blend_target_mask(blend_enable_mask);
}

// 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)
Expand Down
2 changes: 2 additions & 0 deletions rpcs3/Emu/RSX/Core/RSXDriverState.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace rsx
zeta_address_is_cyclic = (1 << 26), // The currently bound Z buffer is active for R/W in a cyclic manner
zeta_address_cyclic_barrier = (1 << 27), // A memory barrier is required to "end" the Z buffer cyclic state

blend_config_dirty = (1 << 28), // Blending configuration has been updated. Only matters when programmable blending is in use.

// TODO - Should signal that we simply need to do a FP compare before the next draw call and invalidate the ucode if the content has changed.
// Marking as dirty to invalidate hot cache also works, it's not like there's tons of barriers per frame anyway.
fragment_program_needs_rehash = fragment_program_ucode_dirty,
Expand Down
6 changes: 3 additions & 3 deletions rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS)

if (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE)
{
const auto frag_depth_type = (m_prog.ctrl & RSX_SHADER_CONTROL_MULTISAMPLED_ZBUFFER)
const auto frag_depth_type = (m_prog.ctrl & RSX_SHADER_CONTROL_ROP_MULTISAMPLED)
? "sampler2DMS"
: "sampler2D";

Expand Down Expand Up @@ -249,7 +249,7 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS)
m_shader_props.require_alpha_kill = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL);
m_shader_props.require_color_format_convert = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT);
m_shader_props.emulate_depth_compare = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE);
m_shader_props.depth_buffer_multisampled = !!(m_prog.ctrl & RSX_SHADER_CONTROL_MULTISAMPLED_ZBUFFER);
m_shader_props.ROP_output_multisampled = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ROP_MULTISAMPLED);

glsl::insert_glsl_legacy_function(OS, m_shader_props);
}
Expand Down Expand Up @@ -344,7 +344,7 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
OS << "void main()\n";
OS << "{\n";

::glsl::insert_rop_init(OS);
::glsl::insert_rop_init(OS, m_prog.mrt_buffers_count);

OS << "\n" << " fs_main();\n\n";

Expand Down
8 changes: 4 additions & 4 deletions rpcs3/Emu/RSX/GL/GLRenderTargets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ color_format rsx::internals::surface_color_format_to_gl(rsx::surface_color_forma
case rsx::surface_color_format::a8r8g8b8:
return{ ::gl::texture::type::uint_8_8_8_8_rev, ::gl::texture::format::bgra, ::gl::texture::internal_format::bgra8, true };

//These formats discard their alpha component, forced to 0 or 1
//All XBGR formats will have remapping before they can be read back in shaders as DRGB8
//Prefix o = 1, z = 0
// These formats discard their alpha component, forced to 0 or 1
// All XBGR formats will have remapping before they can be read back in shaders as DRGB8
// Prefix o = 1, z = 0
case rsx::surface_color_format::x1r5g5b5_o1r5g5b5:
return{ ::gl::texture::type::ushort_5_5_5_1, ::gl::texture::format::rgb, ::gl::texture::internal_format::bgr5a1, true,
{ ::gl::texture::channel::one, ::gl::texture::channel::r, ::gl::texture::channel::g, ::gl::texture::channel::b } };
Expand Down Expand Up @@ -54,7 +54,7 @@ color_format rsx::internals::surface_color_format_to_gl(rsx::surface_color_forma
{ ::gl::texture::channel::one, ::gl::texture::channel::r, ::gl::texture::channel::r, ::gl::texture::channel::r } };

case rsx::surface_color_format::g8b8:
return{ ::gl::texture::type::ubyte, ::gl::texture::format::rg, ::gl::texture::internal_format::rg8, false,
return{ ::gl::texture::type::ubyte, ::gl::texture::format::rg, ::gl::texture::internal_format::rg8, true,
{ ::gl::texture::channel::g, ::gl::texture::channel::r, ::gl::texture::channel::g, ::gl::texture::channel::r } };

case rsx::surface_color_format::x32:
Expand Down
23 changes: 20 additions & 3 deletions rpcs3/Emu/RSX/Program/GLSLCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,13 @@ namespace glsl
;
}

void insert_rop_init(std::ostream& OS)
void insert_rop_init(std::ostream& OS, u32 mrt_buffer_count)
{
program_common::define_glsl_constants<u32>(OS,
{
{ "_MRT_BUFFERS_COUNT", mrt_buffer_count },
});

OS <<
#include "GLSLSnippets/RSXProg/RSXROPPrologue.glsl"
;
Expand Down Expand Up @@ -205,6 +210,8 @@ namespace glsl
{ "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 },
{ "MRT_BLEND_TARGETS_OFFSET ", rsx::ROP_control_bits::MRT_BLEND_TARGETS_OFFSET },
{ "MRT_BLEND_TARGETS_LENGTH ", rsx::ROP_control_bits::MRT_BLEND_TARGETS_NUM_BITS },
{ "ROP_CMD_MASK ", rsx::ROP_control_bits::ROP_CMD_MASK }
});

Expand Down Expand Up @@ -263,9 +270,9 @@ namespace glsl
enabled_options.push_back("_ENABLE_DEPTH_COMPARE");
}

if (props.depth_buffer_multisampled)
if (props.ROP_output_multisampled)
{
enabled_options.push_back("_ENABLE_DEPTH_BUFFER_MULTISAMPLED");
enabled_options.push_back("_ENABLE_ROP_OUTPUT_MULTISAMPLED");
}
}

Expand Down Expand Up @@ -346,6 +353,11 @@ namespace glsl
enabled_options.push_back("_ENABLE_ROP_CHANNEL_REMAPPING");
}

if (props.ROP_programmable_blend)
{
enabled_options.push_back("_ENABLE_PROGRAMMABLE_BLENDING");
}

if (props.require_fog_read)
{
program_common::define_glsl_constants<rsx::fog_mode>(OS,
Expand Down Expand Up @@ -493,6 +505,11 @@ namespace glsl
}
}
}

if (props.ROP_programmable_blend)
{
insert_blend_prologue(OS);
}
}

std::string getFunctionImpl(FUNCTION f)
Expand Down
5 changes: 4 additions & 1 deletion rpcs3/Emu/RSX/Program/GLSLCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ namespace rsx
ALPHA_FUNC_OFFSET = 12,
MSAA_SAMPLE_CTRL_OFFSET = 15,
MRT_CHANNEL_REMAP_OFFSET = 17,
MRT_BLEND_TARGETS_OFFSET = 20,

// Data lengths
ALPHA_FUNC_NUM_BITS = 3,
MSAA_SAMPLE_CTRL_NUM_BITS = 2,
MRT_CHANNEL_REMAP_NUM_BITS = 3,
MRT_BLEND_TARGETS_NUM_BITS = 4,

// Meta
ROP_CMD_MASK = 0xF // Commands are encoded in the lower 4 bits
Expand All @@ -54,6 +56,7 @@ namespace rsx
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); }
void set_blend_target_mask(uint mask) { value |= ((mask & 0xF) << ROP_control_bits::MRT_BLEND_TARGETS_OFFSET); }
};
}

Expand All @@ -77,7 +80,7 @@ namespace glsl
std::string getHalfTypeNameImpl(usz elementCount);
std::string compareFunctionImpl(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar = false);
void insert_vertex_input_fetch(std::stringstream& OS, glsl_rules rules, bool glsl4_compliant=true);
void insert_rop_init(std::ostream& OS);
void insert_rop_init(std::ostream& OS, u32 mrt_buffers_count);
void insert_rop(std::ostream& OS, const shader_properties& props);
void insert_glsl_legacy_function(std::ostream& OS, const shader_properties& props);
std::string getFunctionImpl(FUNCTION f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@ R"(
// Default. Used when we're not utilizing native fp16
vec4 round_to_8bit(const in vec4 v4)
{
uvec4 raw = uvec4(max(floor(fma(_fx12_truncate(v4), vec4(255.), vec4(0.5))), vec4(0.)));
const uvec4 raw = uvec4(max(floor(fma(_fx12_truncate(v4), vec4(255.), vec4(0.5))), vec4(0.)));
return vec4(raw) / vec4(255.);
}
#ifndef _32_BIT_OUTPUT
f16vec4 round_to_8bit(const in f16vec4 v4)
{
uvec4 raw = uvec4(max(floor(fma(_fx12_truncate(vec4(v4)), f16vec4(255.), f16vec4(0.5))), vec4(0.)));
const uvec4 raw = uvec4(max(floor(fma(_fx12_truncate(vec4(v4)), f16vec4(255.), f16vec4(0.5))), vec4(0.)));
return f16vec4(raw) / f16vec4(255.);
}
#endif

// Scalar variant of 8-bit rounding is only available for f32.
float round_to_8bit(const in float v)
{
const uint raw = uint(max(floor(fma(_fx12_truncate(v), 255.f, 0.5f)), 0.f));
return float(raw) / 255.f;
}

#endif

#ifdef _DISABLE_EARLY_DISCARD
Expand Down Expand Up @@ -178,6 +186,24 @@ vec4 remap_ROP_output(const in vec4 col, const in uint remap_index)
return vec4(col.rgb, 0.f);
}
}
vec4 remap_ROP_input(const in vec4 col, const in uint remap_index)
{
switch (remap_index)
{
default:
case ROP_REMAP_SWIZZLE_RGBA: // RGBA
return col;
case ROP_REMAP_SWIZZLE_BBBB: // B8
return col.rrrr;
case ROP_REMAP_SWIZZLE_GBGB: // G8B8
return col.rgrg;
case ROP_REMAP_SWIZZLE_RGB1: // RGB1
case ROP_REMAP_SWIZZLE_RGB0: // RGB0
// This seems completely wrong but hwtest shows that blending with DST_ALPHA reads 1 for all XRGB8 formats regardless of the 0/1 values.
// Lucky for us this opens up the optimization opportunity to not need PB for such formats when only DST_A is referenced. SRC_A is another matter altogether.
return vec4(col.rgb, 1.f);
}
}
#endif

)"
Loading
Loading