rsx: Support variable blend target masks with programmable blending

This commit is contained in:
kd-11
2026-09-07 21:09:01 +03:00
committed by kd-11
parent 8e92a8d845
commit 5a43724b44
8 changed files with 54 additions and 25 deletions
+6
View File
@@ -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)
+2
View File
@@ -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,
+2
View File
@@ -210,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 }
});
+3
View File
@@ -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
@@ -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); }
};
}
@@ -40,21 +40,13 @@ R"(
// ================= Post-output color stages =================
#if _MRT_BUFFERS_COUNT >= 1
#ifdef _ENABLE_PROGRAMMABLE_BLENDING
// Stash the raw source data
MRT0_DO(vec4 blend_s0 = col0;)
MRT1_DO(vec4 blend_s1 = col1;)
MRT2_DO(vec4 blend_s2 = col2;)
MRT3_DO(vec4 blend_s3 = col3;)
#endif
// NOTE: Blending happens before output remapping as per hardware tests.
// Sources = raw shader output, Dest = Remapped output from previous draw
#ifdef _ENABLE_PROGRAMMABLE_BLENDING
MRT0_DO(col0 = _mrt_color_t(do_blend(blend_s0, mrt_color0));)
MRT1_DO(col1 = _mrt_color_t(do_blend(blend_s1, mrt_color1));)
MRT2_DO(col2 = _mrt_color_t(do_blend(blend_s2, mrt_color2));)
MRT3_DO(col3 = _mrt_color_t(do_blend(blend_s3, mrt_color3));)
MRT0_BLEND_DO(col0 = _mrt_color_t(do_blend(col0, mrt_color0));)
MRT1_BLEND_DO(col1 = _mrt_color_t(do_blend(col1, mrt_color1));)
MRT2_BLEND_DO(col2 = _mrt_color_t(do_blend(col2, mrt_color2));)
MRT3_BLEND_DO(col3 = _mrt_color_t(do_blend(col3, mrt_color3));)
#endif
#ifdef _ENABLE_ROP_CHANNEL_REMAPPING
@@ -22,7 +22,7 @@ R"(
#if _MRT_BUFFERS_COUNT >= 1
#define MRT0_DO(expr) expr
#else
#define MRT0_DO(expr) expr
#define MRT0_DO(expr)
#endif
#if _MRT_BUFFERS_COUNT >= 2
#define MRT1_DO(expr) expr
@@ -47,23 +47,29 @@ const uint ROP_remap = get_ROP_channel_remap();
#endif
#ifdef _ENABLE_PROGRAMMABLE_BLENDING
#define IS_MRT_BLEND_ENABLED(n) _test_bit(rop_control, (n) + MRT_BLEND_TARGETS_OFFSET)
#define FRAG_LOAD(n) mrt_color##n = subpassLoad(frag_src_##n)
#define MRT0_BLEND_DO(expr) MRT0_DO(if (IS_MRT_BLEND_ENABLED(0)) expr)
#define MRT1_BLEND_DO(expr) MRT1_DO(if (IS_MRT_BLEND_ENABLED(1)) expr)
#define MRT2_BLEND_DO(expr) MRT2_DO(if (IS_MRT_BLEND_ENABLED(2)) expr)
#define MRT3_BLEND_DO(expr) MRT3_DO(if (IS_MRT_BLEND_ENABLED(3)) expr)
MRT0_DO(vec4 mrt_color0;)
MRT1_DO(vec4 mrt_color1;)
MRT2_DO(vec4 mrt_color2;)
MRT3_DO(vec4 mrt_color3;)
MRT0_DO(FRAG_LOAD(0);)
MRT1_DO(FRAG_LOAD(1);)
MRT2_DO(FRAG_LOAD(2);)
MRT3_DO(FRAG_LOAD(3);)
MRT0_BLEND_DO(FRAG_LOAD(0);)
MRT1_BLEND_DO(FRAG_LOAD(1);)
MRT2_BLEND_DO(FRAG_LOAD(2);)
MRT3_BLEND_DO(FRAG_LOAD(3);)
#ifdef _ENABLE_ROP_CHANNEL_REMAPPING
MRT0_DO(mrt_color0 = _mrt_color_t(remap_ROP_input(mrt_color0, ROP_remap));)
MRT1_DO(mrt_color1 = _mrt_color_t(remap_ROP_input(mrt_color1, ROP_remap));)
MRT2_DO(mrt_color2 = _mrt_color_t(remap_ROP_input(mrt_color2, ROP_remap));)
MRT3_DO(mrt_color3 = _mrt_color_t(remap_ROP_input(mrt_color3, ROP_remap));)
MRT0_BLEND_DO(mrt_color0 = _mrt_color_t(remap_ROP_input(mrt_color0, ROP_remap));)
MRT1_BLEND_DO(mrt_color1 = _mrt_color_t(remap_ROP_input(mrt_color1, ROP_remap));)
MRT2_BLEND_DO(mrt_color2 = _mrt_color_t(remap_ROP_input(mrt_color2, ROP_remap));)
MRT3_BLEND_DO(mrt_color3 = _mrt_color_t(remap_ROP_input(mrt_color3, ROP_remap));)
#endif // _ENABLE_ROP_CHANNEL_REMAPPING
#undef FRAG_LOAD
+20 -2
View File
@@ -299,6 +299,9 @@ namespace rsx
return false;
}
const bool is_blend_config_dirty = RSX(ctx)->m_graphics_state.test(rsx::blend_config_dirty);
RSX(ctx)->m_graphics_state.clear(rsx::blend_config_dirty);
// FIXME: Unimplemented
if (RSX(ctx)->get_backend_config().supports_hw_msaa &&
REGS(ctx)->surface_antialias() != rsx::surface_antialiasing::center_1_sample)
@@ -357,6 +360,11 @@ namespace rsx
}
}
if (need_programmable_blending && is_blend_config_dirty)
{
RSX(ctx)->m_graphics_state.set(rsx::fragment_state_dirty);
}
if (need_programmable_blending == programmable_blend_active)
{
return false;
@@ -2189,7 +2197,12 @@ namespace rsx
if (m_graphics_state.test(rsx::pipeline_config_dirty) &&
backend_config.supports_programmable_blending)
{
evaluate_programmable_blending_state(m_ctx, current_fragment_program);
if (evaluate_programmable_blending_state(m_ctx, current_fragment_program) &&
(current_fragment_program.ctrl & RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING))
{
// Reload ROP control
m_graphics_state.set(rsx::pipeline_state::fragment_state_dirty);
}
}
prefetch_vertex_program();
@@ -2333,7 +2346,12 @@ namespace rsx
if (backend_config.supports_programmable_blending)
{
evaluate_programmable_blending_state(m_ctx, current_fragment_program);
if (evaluate_programmable_blending_state(m_ctx, current_fragment_program) &&
!!(current_fragment_program.ctrl & RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING))
{
// Reload ROP control bits. Is this really needed here?
m_graphics_state.set(rsx::pipeline_state::fragment_state_dirty);
}
}
const bool zeta_was_cyclic = m_graphics_state & rsx::zeta_address_is_cyclic;
+2 -2
View File
@@ -775,8 +775,8 @@ namespace rsx
state_signals[NV4097_SET_ZMIN_MAX_CONTROL] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_LOGIC_OP_ENABLE] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_LOGIC_OP] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_BLEND_ENABLE] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_BLEND_ENABLE_MRT] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_BLEND_ENABLE] = rsx::pipeline_config_dirty | rsx::blend_config_dirty;
state_signals[NV4097_SET_BLEND_ENABLE_MRT] = rsx::pipeline_config_dirty | rsx::blend_config_dirty;
state_signals[NV4097_SET_STENCIL_FUNC] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_BACK_STENCIL_FUNC] = rsx::pipeline_config_dirty;
state_signals[NV4097_SET_RESTART_INDEX_ENABLE] = rsx::pipeline_config_dirty;