[GPU] Fix min/max blend operations to properly apply factors
Applies factors before the MIN/MAX operation - in FSI/ROV via runtime blending, in FBO/RTV by pre-multiplying shader output when dstFactor is ONE only, as we don't have access to dest to provide full support as we do in the FSI/ROV paths.
This commit is contained in:
@@ -947,6 +947,38 @@ PipelineCache::GetCurrentPixelShaderModification(
|
||||
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if MIN/MAX blend is used with non-trivial source factors.
|
||||
// D3D12 fixed-function blend ignores factors for MIN/MAX, but Xbox 360
|
||||
// applies them. If the destination factor is ONE (or ZERO), we can
|
||||
// pre-multiply the shader output by the source factor to emulate this.
|
||||
// Only RT0 is supported for now.
|
||||
modification.pixel.rt0_blend_rgb_factor_for_premult =
|
||||
xenos::BlendFactor::kOne;
|
||||
modification.pixel.rt0_blend_a_factor_for_premult =
|
||||
xenos::BlendFactor::kOne;
|
||||
|
||||
if (shader.writes_color_target(0)) {
|
||||
auto blend_control = regs.Get<reg::RB_BLENDCONTROL>(
|
||||
reg::RB_BLENDCONTROL::rt_register_indices[0]);
|
||||
|
||||
// Pre-multiply by kSrcAlpha for MIN/MAX blend ops when dstFactor is ONE.
|
||||
if ((blend_control.color_comb_fcn == xenos::BlendOp::kMin ||
|
||||
blend_control.color_comb_fcn == xenos::BlendOp::kMax) &&
|
||||
blend_control.color_srcblend == xenos::BlendFactor::kSrcAlpha &&
|
||||
blend_control.color_destblend == xenos::BlendFactor::kOne) {
|
||||
modification.pixel.rt0_blend_rgb_factor_for_premult =
|
||||
xenos::BlendFactor::kSrcAlpha;
|
||||
}
|
||||
|
||||
if ((blend_control.alpha_comb_fcn == xenos::BlendOp::kMin ||
|
||||
blend_control.alpha_comb_fcn == xenos::BlendOp::kMax) &&
|
||||
blend_control.alpha_srcblend == xenos::BlendFactor::kSrcAlpha &&
|
||||
blend_control.alpha_destblend == xenos::BlendFactor::kOne) {
|
||||
modification.pixel.rt0_blend_a_factor_for_premult =
|
||||
xenos::BlendFactor::kSrcAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modification;
|
||||
|
||||
@@ -114,7 +114,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// If anything in this is structure is changed in a way not compatible with
|
||||
// the previous layout, invalidate the pipeline storages by increasing this
|
||||
// version number (0xYYYYMMDD)!
|
||||
static constexpr uint32_t kVersion = 0x20220720;
|
||||
static constexpr uint32_t kVersion = 0x20251216;
|
||||
|
||||
enum class DepthStencilMode : uint32_t {
|
||||
kNoModifiers,
|
||||
@@ -179,6 +179,12 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
uint32_t dynamic_addressable_register_count : 8;
|
||||
// Non-ROV - depth / stencil output mode.
|
||||
DepthStencilMode depth_stencil_mode : 2;
|
||||
// For host render targets with MIN/MAX blend op - the source blend factor
|
||||
// to pre-multiply the shader output by (since D3D12 MIN/MAX ignores blend
|
||||
// factors, but Xbox 360 applies them). kOne means no pre-multiply.
|
||||
// Only RT0 is supported for now.
|
||||
xenos::BlendFactor rt0_blend_rgb_factor_for_premult : 5;
|
||||
xenos::BlendFactor rt0_blend_a_factor_for_premult : 5;
|
||||
} pixel;
|
||||
|
||||
explicit Modification(uint64_t modification_value = 0)
|
||||
|
||||
@@ -1701,6 +1701,143 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs() {
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
// For RT0 with MIN/MAX blend op, pre-multiply by the source blend factor
|
||||
// (since D3D12 MIN/MAX ignores blend factors, but Xbox 360 applies them).
|
||||
if (i == 0 && !edram_rov_used_) {
|
||||
xenos::BlendFactor rgb_factor_for_premult =
|
||||
GetDxbcShaderModification().pixel.rt0_blend_rgb_factor_for_premult;
|
||||
xenos::BlendFactor a_factor_for_premult =
|
||||
GetDxbcShaderModification().pixel.rt0_blend_a_factor_for_premult;
|
||||
bool premult_rgb = rgb_factor_for_premult != xenos::BlendFactor::kOne;
|
||||
bool premult_a = a_factor_for_premult != xenos::BlendFactor::kOne;
|
||||
if (premult_rgb || premult_a) {
|
||||
uint32_t premult_temp = PushSystemTemp();
|
||||
// Compute and apply RGB factor.
|
||||
if (premult_rgb) {
|
||||
switch (rgb_factor_for_premult) {
|
||||
case xenos::BlendFactor::kZero:
|
||||
a_.OpMov(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::LF(0.0f));
|
||||
break;
|
||||
case xenos::BlendFactor::kSrcColor:
|
||||
// Multiply by itself (square).
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
dxbc::Src::R(system_temp_color));
|
||||
break;
|
||||
case xenos::BlendFactor::kOneMinusSrcColor:
|
||||
a_.OpAdd(dxbc::Dest::R(premult_temp, 0b0111), dxbc::Src::LF(1.0f),
|
||||
-dxbc::Src::R(system_temp_color));
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
dxbc::Src::R(premult_temp));
|
||||
break;
|
||||
case xenos::BlendFactor::kSrcAlpha:
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW));
|
||||
break;
|
||||
case xenos::BlendFactor::kOneMinusSrcAlpha:
|
||||
a_.OpAdd(dxbc::Dest::R(premult_temp, 0b0001), dxbc::Src::LF(1.0f),
|
||||
-dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW));
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
dxbc::Src::R(premult_temp, dxbc::Src::kXXXX));
|
||||
break;
|
||||
case xenos::BlendFactor::kConstantColor:
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
LoadSystemConstant(
|
||||
SystemConstants::Index::kEdramBlendConstant,
|
||||
offsetof(SystemConstants, edram_blend_constant),
|
||||
dxbc::Src::kXYZW));
|
||||
break;
|
||||
case xenos::BlendFactor::kOneMinusConstantColor:
|
||||
a_.OpAdd(dxbc::Dest::R(premult_temp, 0b0111), dxbc::Src::LF(1.0f),
|
||||
-LoadSystemConstant(
|
||||
SystemConstants::Index::kEdramBlendConstant,
|
||||
offsetof(SystemConstants, edram_blend_constant),
|
||||
dxbc::Src::kXYZW));
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
dxbc::Src::R(premult_temp));
|
||||
break;
|
||||
case xenos::BlendFactor::kConstantAlpha:
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
LoadSystemConstant(
|
||||
SystemConstants::Index::kEdramBlendConstant,
|
||||
offsetof(SystemConstants, edram_blend_constant),
|
||||
dxbc::Src::kWWWW));
|
||||
break;
|
||||
case xenos::BlendFactor::kOneMinusConstantAlpha:
|
||||
a_.OpAdd(dxbc::Dest::R(premult_temp, 0b0001), dxbc::Src::LF(1.0f),
|
||||
-LoadSystemConstant(
|
||||
SystemConstants::Index::kEdramBlendConstant,
|
||||
offsetof(SystemConstants, edram_blend_constant),
|
||||
dxbc::Src::kWWWW));
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color),
|
||||
dxbc::Src::R(premult_temp, dxbc::Src::kXXXX));
|
||||
break;
|
||||
default:
|
||||
// kOne or unsupported - no pre-multiply.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Compute and apply alpha factor.
|
||||
if (premult_a) {
|
||||
switch (a_factor_for_premult) {
|
||||
case xenos::BlendFactor::kZero:
|
||||
a_.OpMov(dxbc::Dest::R(system_temp_color, 0b1000),
|
||||
dxbc::Src::LF(0.0f));
|
||||
break;
|
||||
case xenos::BlendFactor::kSrcColor:
|
||||
case xenos::BlendFactor::kSrcAlpha:
|
||||
// Alpha * Alpha.
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b1000),
|
||||
dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW),
|
||||
dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW));
|
||||
break;
|
||||
case xenos::BlendFactor::kOneMinusSrcColor:
|
||||
case xenos::BlendFactor::kOneMinusSrcAlpha:
|
||||
a_.OpAdd(dxbc::Dest::R(premult_temp, 0b0001), dxbc::Src::LF(1.0f),
|
||||
-dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW));
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b1000),
|
||||
dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW),
|
||||
dxbc::Src::R(premult_temp, dxbc::Src::kXXXX));
|
||||
break;
|
||||
case xenos::BlendFactor::kConstantColor:
|
||||
case xenos::BlendFactor::kConstantAlpha:
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b1000),
|
||||
dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW),
|
||||
LoadSystemConstant(
|
||||
SystemConstants::Index::kEdramBlendConstant,
|
||||
offsetof(SystemConstants, edram_blend_constant),
|
||||
dxbc::Src::kWWWW));
|
||||
break;
|
||||
case xenos::BlendFactor::kOneMinusConstantColor:
|
||||
case xenos::BlendFactor::kOneMinusConstantAlpha:
|
||||
a_.OpAdd(dxbc::Dest::R(premult_temp, 0b0001), dxbc::Src::LF(1.0f),
|
||||
-LoadSystemConstant(
|
||||
SystemConstants::Index::kEdramBlendConstant,
|
||||
offsetof(SystemConstants, edram_blend_constant),
|
||||
dxbc::Src::kWWWW));
|
||||
a_.OpMul(dxbc::Dest::R(system_temp_color, 0b1000),
|
||||
dxbc::Src::R(system_temp_color, dxbc::Src::kWWWW),
|
||||
dxbc::Src::R(premult_temp, dxbc::Src::kXXXX));
|
||||
break;
|
||||
case xenos::BlendFactor::kSrcAlphaSaturate:
|
||||
// For alpha, SrcAlphaSaturate is 1.0, so no pre-multiply needed.
|
||||
break;
|
||||
default:
|
||||
// kOne or unsupported - no pre-multiply.
|
||||
break;
|
||||
}
|
||||
}
|
||||
PopSystemTemp(); // premult_temp
|
||||
}
|
||||
}
|
||||
// Copy the color from a readable temp register to an output register.
|
||||
a_.OpMov(dxbc::Dest::O(i), dxbc::Src::R(system_temp_color));
|
||||
}
|
||||
@@ -2526,33 +2663,126 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
||||
rt_clamp_vec_src.Select(2));
|
||||
}
|
||||
// Need to do min/max for color.
|
||||
// Note: Unlike Vulkan/D3D12 fixed-function blend which ignores
|
||||
// factors for MIN/MAX, the Xbox 360 applies blend factors before
|
||||
// min/max.
|
||||
a_.OpElse();
|
||||
{
|
||||
// Extract the color min (0) or max (1) bit to temp.x
|
||||
// temp.x = whether min or max should be used for color.
|
||||
uint32_t blend_src_temp = PushSystemTemp();
|
||||
dxbc::Dest blend_src_temp_rgb_dest(
|
||||
dxbc::Dest::R(blend_src_temp, 0b0111));
|
||||
dxbc::Src blend_src_temp_src(dxbc::Src::R(blend_src_temp));
|
||||
|
||||
// Apply source color factor for min/max.
|
||||
// Extract the source color factor to temp.x.
|
||||
a_.OpAnd(temp_x_dest, rt_blend_factors_ops_src,
|
||||
dxbc::Src::LU(1 << 5));
|
||||
// Check if need to do min or max for color.
|
||||
// temp.x = free.
|
||||
dxbc::Src::LU((1 << 5) - 1));
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
// Choose max of the colors without applying the factors to
|
||||
// color_temp.xyz.
|
||||
// color_temp.xyz = blended color.
|
||||
a_.OpMax(color_temp_rgb_dest,
|
||||
dxbc::Src::R(system_temps_color_[i]), color_temp_src);
|
||||
a_.OpSwitch(temp_x_src);
|
||||
ROV_HandleColorBlendFactorCases(system_temps_color_[i],
|
||||
color_temp, blend_src_temp);
|
||||
a_.OpEndSwitch();
|
||||
// Check if fixed-point and needs clamping.
|
||||
a_.OpAnd(
|
||||
temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointColor));
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
a_.OpMax(blend_src_temp_rgb_dest, blend_src_temp_src,
|
||||
rt_clamp_vec_src.Select(0));
|
||||
a_.OpMin(blend_src_temp_rgb_dest, blend_src_temp_src,
|
||||
rt_clamp_vec_src.Select(2));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
// Multiply source by factor.
|
||||
a_.OpMul(blend_src_temp_rgb_dest,
|
||||
dxbc::Src::R(system_temps_color_[i]),
|
||||
blend_src_temp_src);
|
||||
// Clamp result if fixed-point.
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
a_.OpMax(blend_src_temp_rgb_dest, blend_src_temp_src,
|
||||
rt_clamp_vec_src.Select(0));
|
||||
a_.OpMin(blend_src_temp_rgb_dest, blend_src_temp_src,
|
||||
rt_clamp_vec_src.Select(2));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
// Need to do min.
|
||||
a_.OpElse();
|
||||
{
|
||||
// Choose min of the colors without applying the factors to
|
||||
// color_temp.xyz.
|
||||
// color_temp.xyz = blended color.
|
||||
a_.OpMin(color_temp_rgb_dest,
|
||||
dxbc::Src::R(system_temps_color_[i]), color_temp_src);
|
||||
a_.OpMov(blend_src_temp_rgb_dest, dxbc::Src::LF(0.0f));
|
||||
}
|
||||
// Close the min or max check.
|
||||
a_.OpEndIf();
|
||||
|
||||
// Apply destination color factor for min/max.
|
||||
uint32_t blend_dest_temp = PushSystemTemp();
|
||||
dxbc::Dest blend_dest_temp_rgb_dest(
|
||||
dxbc::Dest::R(blend_dest_temp, 0b0111));
|
||||
dxbc::Src blend_dest_temp_src(dxbc::Src::R(blend_dest_temp));
|
||||
|
||||
// Extract the destination color factor to temp.x.
|
||||
a_.OpUBFE(temp_x_dest, dxbc::Src::LU(5), dxbc::Src::LU(8),
|
||||
rt_blend_factors_ops_src);
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
a_.OpSwitch(temp_x_src);
|
||||
ROV_HandleColorBlendFactorCases(system_temps_color_[i],
|
||||
color_temp, blend_dest_temp);
|
||||
a_.OpEndSwitch();
|
||||
// Check if fixed-point and needs clamping.
|
||||
a_.OpAnd(
|
||||
temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointColor));
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
a_.OpMax(blend_dest_temp_rgb_dest, blend_dest_temp_src,
|
||||
rt_clamp_vec_src.Select(0));
|
||||
a_.OpMin(blend_dest_temp_rgb_dest, blend_dest_temp_src,
|
||||
rt_clamp_vec_src.Select(2));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
// Multiply destination by factor.
|
||||
a_.OpMul(blend_dest_temp_rgb_dest, color_temp_src,
|
||||
blend_dest_temp_src);
|
||||
// Clamp result if fixed-point.
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
a_.OpMax(blend_dest_temp_rgb_dest, blend_dest_temp_src,
|
||||
rt_clamp_vec_src.Select(0));
|
||||
a_.OpMin(blend_dest_temp_rgb_dest, blend_dest_temp_src,
|
||||
rt_clamp_vec_src.Select(2));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
a_.OpElse();
|
||||
{
|
||||
a_.OpMov(blend_dest_temp_rgb_dest, dxbc::Src::LF(0.0f));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
|
||||
// Now do min or max on the factored values.
|
||||
// Extract the color min (0) or max (1) bit to temp.x.
|
||||
a_.OpAnd(temp_x_dest, rt_blend_factors_ops_src,
|
||||
dxbc::Src::LU(1 << 5));
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
// MAX: color_temp.xyz = max(src * srcFactor, dst * dstFactor)
|
||||
a_.OpMax(color_temp_rgb_dest, blend_src_temp_src,
|
||||
blend_dest_temp_src);
|
||||
}
|
||||
a_.OpElse();
|
||||
{
|
||||
// MIN: color_temp.xyz = min(src * srcFactor, dst * dstFactor)
|
||||
a_.OpMin(color_temp_rgb_dest, blend_src_temp_src,
|
||||
blend_dest_temp_src);
|
||||
}
|
||||
a_.OpEndIf();
|
||||
|
||||
PopSystemTemp(); // blend_dest_temp
|
||||
PopSystemTemp(); // blend_src_temp
|
||||
}
|
||||
// Close the color factor blending or min/max check.
|
||||
a_.OpEndIf();
|
||||
@@ -2731,34 +2961,114 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
||||
rt_clamp_vec_src.Select(3));
|
||||
}
|
||||
// Need to do min/max for alpha.
|
||||
// Note: Unlike Vulkan/D3D12 fixed-function blend which ignores
|
||||
// factors for MIN/MAX, the Xbox 360 applies blend factors before
|
||||
// min/max.
|
||||
a_.OpElse();
|
||||
{
|
||||
// Extract the alpha min (0) or max (1) bit to temp.x.
|
||||
// temp.x = whether min or max should be used for alpha.
|
||||
a_.OpAnd(temp_x_dest, rt_blend_factors_ops_src,
|
||||
dxbc::Src::LU(1 << 21));
|
||||
// Check if need to do min or max for alpha.
|
||||
// temp.x = free.
|
||||
// We'll use temp.x for source alpha (factored) and temp.y for
|
||||
// destination alpha (factored).
|
||||
|
||||
// Apply source alpha factor for min/max.
|
||||
// Extract the source alpha factor to temp.x (bits 16-20).
|
||||
a_.OpUBFE(temp_x_dest, dxbc::Src::LU(5), dxbc::Src::LU(16),
|
||||
rt_blend_factors_ops_src);
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
// Choose max of the alphas without applying the factors to
|
||||
// color_temp.w.
|
||||
// color_temp.w = blended alpha.
|
||||
a_.OpMax(color_temp_a_dest,
|
||||
a_.OpSwitch(temp_x_src);
|
||||
ROV_HandleAlphaBlendFactorCases(system_temps_color_[i],
|
||||
color_temp, temp, 0);
|
||||
a_.OpEndSwitch();
|
||||
// Check if fixed-point and needs clamping.
|
||||
uint32_t alpha_is_fixed_temp = PushSystemTemp();
|
||||
a_.OpAnd(
|
||||
dxbc::Dest::R(alpha_is_fixed_temp, 0b0001),
|
||||
rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointAlpha));
|
||||
a_.OpIf(true,
|
||||
dxbc::Src::R(alpha_is_fixed_temp, dxbc::Src::kXXXX));
|
||||
{
|
||||
a_.OpMax(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(1));
|
||||
a_.OpMin(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(3));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
// Multiply source alpha by factor.
|
||||
a_.OpMul(temp_x_dest,
|
||||
dxbc::Src::R(system_temps_color_[i], dxbc::Src::kWWWW),
|
||||
color_temp_a_src);
|
||||
temp_x_src);
|
||||
// Clamp result if fixed-point.
|
||||
a_.OpIf(true,
|
||||
dxbc::Src::R(alpha_is_fixed_temp, dxbc::Src::kXXXX));
|
||||
PopSystemTemp(); // alpha_is_fixed_temp
|
||||
{
|
||||
a_.OpMax(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(1));
|
||||
a_.OpMin(temp_x_dest, temp_x_src, rt_clamp_vec_src.Select(3));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
// Need to do min.
|
||||
a_.OpElse();
|
||||
{
|
||||
// Choose min of the alphas without applying the factors to
|
||||
// color_temp.w.
|
||||
// color_temp.w = blended alpha.
|
||||
a_.OpMin(color_temp_a_dest,
|
||||
dxbc::Src::R(system_temps_color_[i], dxbc::Src::kWWWW),
|
||||
color_temp_a_src);
|
||||
a_.OpMov(temp_x_dest, dxbc::Src::LF(0.0f));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
|
||||
// Apply destination alpha factor for min/max.
|
||||
// Extract the destination alpha factor to temp.y (bits 24-28).
|
||||
a_.OpUBFE(temp_y_dest, dxbc::Src::LU(5), dxbc::Src::LU(24),
|
||||
rt_blend_factors_ops_src);
|
||||
a_.OpIf(true, temp_y_src);
|
||||
{
|
||||
a_.OpSwitch(temp_y_src);
|
||||
ROV_HandleAlphaBlendFactorCases(system_temps_color_[i],
|
||||
color_temp, temp, 1);
|
||||
a_.OpEndSwitch();
|
||||
// Check if fixed-point and needs clamping.
|
||||
uint32_t alpha_is_fixed_temp2 = PushSystemTemp();
|
||||
a_.OpAnd(
|
||||
dxbc::Dest::R(alpha_is_fixed_temp2, 0b0001),
|
||||
rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointAlpha));
|
||||
a_.OpIf(true,
|
||||
dxbc::Src::R(alpha_is_fixed_temp2, dxbc::Src::kXXXX));
|
||||
{
|
||||
a_.OpMax(temp_y_dest, temp_y_src, rt_clamp_vec_src.Select(1));
|
||||
a_.OpMin(temp_y_dest, temp_y_src, rt_clamp_vec_src.Select(3));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
// Multiply destination alpha by factor.
|
||||
a_.OpMul(temp_y_dest, color_temp_a_src, temp_y_src);
|
||||
// Clamp result if fixed-point.
|
||||
a_.OpIf(true,
|
||||
dxbc::Src::R(alpha_is_fixed_temp2, dxbc::Src::kXXXX));
|
||||
PopSystemTemp(); // alpha_is_fixed_temp2
|
||||
{
|
||||
a_.OpMax(temp_y_dest, temp_y_src, rt_clamp_vec_src.Select(1));
|
||||
a_.OpMin(temp_y_dest, temp_y_src, rt_clamp_vec_src.Select(3));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
a_.OpElse();
|
||||
{
|
||||
a_.OpMov(temp_y_dest, dxbc::Src::LF(0.0f));
|
||||
}
|
||||
a_.OpEndIf();
|
||||
|
||||
// Now do min or max on the factored alpha values.
|
||||
// Extract the alpha min (0) or max (1) bit to color_temp.w.
|
||||
a_.OpAnd(color_temp_a_dest, rt_blend_factors_ops_src,
|
||||
dxbc::Src::LU(1 << 21));
|
||||
a_.OpIf(true, color_temp_a_src);
|
||||
{
|
||||
// MAX: color_temp.w = max(src * srcFactor, dst * dstFactor)
|
||||
a_.OpMax(color_temp_a_dest, temp_x_src, temp_y_src);
|
||||
}
|
||||
a_.OpElse();
|
||||
{
|
||||
// MIN: color_temp.w = min(src * srcFactor, dst * dstFactor)
|
||||
a_.OpMin(color_temp_a_dest, temp_x_src, temp_y_src);
|
||||
}
|
||||
// Close the min or max check.
|
||||
a_.OpEndIf();
|
||||
}
|
||||
// Close the alpha factor blending or min/max check.
|
||||
|
||||
@@ -34,7 +34,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
|
||||
// prototyping stage (easier to do small granular updates with an
|
||||
// incremental counter).
|
||||
static constexpr uint32_t kVersion = 7;
|
||||
static constexpr uint32_t kVersion = 8;
|
||||
|
||||
enum class DepthStencilMode : uint32_t {
|
||||
kNoModifiers,
|
||||
@@ -83,6 +83,12 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
uint32_t param_gen_point : 1;
|
||||
// For host render targets - depth / stencil output mode.
|
||||
DepthStencilMode depth_stencil_mode : 3;
|
||||
// For host render targets with MIN/MAX blend op - the source blend factor
|
||||
// to pre-multiply the shader output by (since Vulkan/D3D12 MIN/MAX
|
||||
// ignores blend factors, but Xbox 360 applies them). kOne means no
|
||||
// pre-multiply. Only RT0 is supported for now.
|
||||
xenos::BlendFactor rt0_blend_rgb_factor_for_premult : 5;
|
||||
xenos::BlendFactor rt0_blend_a_factor_for_premult : 5;
|
||||
} pixel;
|
||||
uint64_t value = 0;
|
||||
|
||||
|
||||
@@ -1301,6 +1301,12 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() {
|
||||
// FBO path: Copy from Function-scoped variables to Output variables.
|
||||
// This is done at the end after alpha test/coverage so we can read the
|
||||
// color values during those operations.
|
||||
Modification shader_modification = GetSpirvShaderModification();
|
||||
xenos::BlendFactor rt0_rgb_premult_factor =
|
||||
shader_modification.pixel.rt0_blend_rgb_factor_for_premult;
|
||||
xenos::BlendFactor rt0_a_premult_factor =
|
||||
shader_modification.pixel.rt0_blend_a_factor_for_premult;
|
||||
|
||||
uint32_t color_targets_to_copy = current_shader().writes_color_targets();
|
||||
uint32_t color_target_index;
|
||||
while (xe::bit_scan_forward(color_targets_to_copy, &color_target_index)) {
|
||||
@@ -1308,8 +1314,175 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() {
|
||||
spv::Id var_color = output_or_var_fragment_data_[color_target_index];
|
||||
spv::Id out_color = output_fragment_data_[color_target_index];
|
||||
if (var_color != spv::NoResult && out_color != spv::NoResult) {
|
||||
builder_->createStore(builder_->createLoad(var_color, spv::NoPrecision),
|
||||
out_color);
|
||||
spv::Id color = builder_->createLoad(var_color, spv::NoPrecision);
|
||||
|
||||
// For RT0, apply pre-multiply by source blend factor if needed for
|
||||
// MIN/MAX blend emulation (since Vulkan/D3D12 ignores blend factors
|
||||
// for MIN/MAX but Xbox 360 applies them).
|
||||
if (color_target_index == 0 &&
|
||||
(rt0_rgb_premult_factor != xenos::BlendFactor::kOne ||
|
||||
rt0_a_premult_factor != xenos::BlendFactor::kOne)) {
|
||||
// Helper to extract RGB (xyz) from a float4.
|
||||
auto extract_rgb = [&](spv::Id vec4) -> spv::Id {
|
||||
uint_vector_temp_.clear();
|
||||
uint_vector_temp_.push_back(0);
|
||||
uint_vector_temp_.push_back(1);
|
||||
uint_vector_temp_.push_back(2);
|
||||
return builder_->createRvalueSwizzle(spv::NoPrecision, type_float3_,
|
||||
vec4, uint_vector_temp_);
|
||||
};
|
||||
|
||||
// Get blend factor values.
|
||||
auto get_factor_value = [&](xenos::BlendFactor factor,
|
||||
bool for_alpha) -> spv::Id {
|
||||
spv::Id src_color = color;
|
||||
switch (factor) {
|
||||
case xenos::BlendFactor::kZero:
|
||||
return for_alpha ? const_float_0_ : const_float3_0_;
|
||||
case xenos::BlendFactor::kOne:
|
||||
return for_alpha ? const_float_1_ : const_float3_1_;
|
||||
case xenos::BlendFactor::kSrcColor:
|
||||
return for_alpha ? builder_->createCompositeExtract(
|
||||
src_color, type_float_, 3)
|
||||
: extract_rgb(src_color);
|
||||
case xenos::BlendFactor::kOneMinusSrcColor: {
|
||||
spv::Id src = for_alpha ? builder_->createCompositeExtract(
|
||||
src_color, type_float_, 3)
|
||||
: extract_rgb(src_color);
|
||||
spv::Id one = for_alpha ? const_float_1_ : const_float3_1_;
|
||||
return builder_->createBinOp(
|
||||
spv::OpFSub, for_alpha ? type_float_ : type_float3_, one,
|
||||
src);
|
||||
}
|
||||
case xenos::BlendFactor::kSrcAlpha: {
|
||||
spv::Id alpha =
|
||||
builder_->createCompositeExtract(src_color, type_float_, 3);
|
||||
if (for_alpha) {
|
||||
return alpha;
|
||||
}
|
||||
return builder_->smearScalar(spv::NoPrecision, alpha,
|
||||
type_float3_);
|
||||
}
|
||||
case xenos::BlendFactor::kOneMinusSrcAlpha: {
|
||||
spv::Id alpha =
|
||||
builder_->createCompositeExtract(src_color, type_float_, 3);
|
||||
spv::Id one_minus_alpha = builder_->createBinOp(
|
||||
spv::OpFSub, type_float_, const_float_1_, alpha);
|
||||
if (for_alpha) {
|
||||
return one_minus_alpha;
|
||||
}
|
||||
return builder_->smearScalar(spv::NoPrecision, one_minus_alpha,
|
||||
type_float3_);
|
||||
}
|
||||
case xenos::BlendFactor::kConstantColor:
|
||||
case xenos::BlendFactor::kConstantAlpha: {
|
||||
// Load blend constant from system constants.
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(builder_->makeIntConstant(
|
||||
kSystemConstantEdramBlendConstant));
|
||||
spv::Id blend_constant = builder_->createLoad(
|
||||
builder_->createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants_,
|
||||
id_vector_temp_),
|
||||
spv::NoPrecision);
|
||||
if (factor == xenos::BlendFactor::kConstantAlpha) {
|
||||
spv::Id alpha = builder_->createCompositeExtract(
|
||||
blend_constant, type_float_, 3);
|
||||
if (for_alpha) {
|
||||
return alpha;
|
||||
}
|
||||
return builder_->smearScalar(spv::NoPrecision, alpha,
|
||||
type_float3_);
|
||||
}
|
||||
if (for_alpha) {
|
||||
return builder_->createCompositeExtract(blend_constant,
|
||||
type_float_, 3);
|
||||
}
|
||||
return extract_rgb(blend_constant);
|
||||
}
|
||||
case xenos::BlendFactor::kOneMinusConstantColor:
|
||||
case xenos::BlendFactor::kOneMinusConstantAlpha: {
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(builder_->makeIntConstant(
|
||||
kSystemConstantEdramBlendConstant));
|
||||
spv::Id blend_constant = builder_->createLoad(
|
||||
builder_->createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants_,
|
||||
id_vector_temp_),
|
||||
spv::NoPrecision);
|
||||
spv::Id constant_value;
|
||||
if (factor == xenos::BlendFactor::kOneMinusConstantAlpha) {
|
||||
spv::Id alpha = builder_->createCompositeExtract(
|
||||
blend_constant, type_float_, 3);
|
||||
if (for_alpha) {
|
||||
constant_value = alpha;
|
||||
} else {
|
||||
constant_value = builder_->smearScalar(spv::NoPrecision,
|
||||
alpha, type_float3_);
|
||||
}
|
||||
} else {
|
||||
if (for_alpha) {
|
||||
constant_value = builder_->createCompositeExtract(
|
||||
blend_constant, type_float_, 3);
|
||||
} else {
|
||||
constant_value = extract_rgb(blend_constant);
|
||||
}
|
||||
}
|
||||
spv::Id one = for_alpha ? const_float_1_ : const_float3_1_;
|
||||
return builder_->createBinOp(
|
||||
spv::OpFSub, for_alpha ? type_float_ : type_float3_, one,
|
||||
constant_value);
|
||||
}
|
||||
default:
|
||||
// Unsupported factors - return 1 (no multiply).
|
||||
return for_alpha ? const_float_1_ : const_float3_1_;
|
||||
}
|
||||
};
|
||||
|
||||
// Apply RGB pre-multiply.
|
||||
if (rt0_rgb_premult_factor != xenos::BlendFactor::kOne) {
|
||||
spv::Id rgb_factor =
|
||||
get_factor_value(rt0_rgb_premult_factor, false);
|
||||
spv::Id rgb = extract_rgb(color);
|
||||
rgb = builder_->createBinOp(spv::OpFMul, type_float3_, rgb,
|
||||
rgb_factor);
|
||||
// Reconstruct float4 with new RGB and original alpha.
|
||||
spv::Id alpha =
|
||||
builder_->createCompositeExtract(color, type_float_, 3);
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(
|
||||
builder_->createCompositeExtract(rgb, type_float_, 0));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->createCompositeExtract(rgb, type_float_, 1));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->createCompositeExtract(rgb, type_float_, 2));
|
||||
id_vector_temp_.push_back(alpha);
|
||||
color = builder_->createCompositeConstruct(type_float4_,
|
||||
id_vector_temp_);
|
||||
}
|
||||
|
||||
// Apply alpha pre-multiply.
|
||||
if (rt0_a_premult_factor != xenos::BlendFactor::kOne) {
|
||||
spv::Id a_factor = get_factor_value(rt0_a_premult_factor, true);
|
||||
spv::Id alpha =
|
||||
builder_->createCompositeExtract(color, type_float_, 3);
|
||||
alpha = builder_->createBinOp(spv::OpFMul, type_float_, alpha,
|
||||
a_factor);
|
||||
// Replace alpha in color (extract RGB, reconstruct with new alpha).
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(
|
||||
builder_->createCompositeExtract(color, type_float_, 0));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->createCompositeExtract(color, type_float_, 1));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->createCompositeExtract(color, type_float_, 2));
|
||||
id_vector_temp_.push_back(alpha);
|
||||
color = builder_->createCompositeConstruct(type_float4_,
|
||||
id_vector_temp_);
|
||||
}
|
||||
}
|
||||
|
||||
builder_->createStore(color, out_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3346,142 +3519,113 @@ spv::Id SpirvShaderTranslator::FSI_BlendColorOrAlphaWithUnclampedResult(
|
||||
constant_color_clamped != spv::NoResult));
|
||||
spv::Id value_type = is_alpha ? type_float_ : type_float3_;
|
||||
|
||||
// Handle min and max blend operations, which don't involve the factors.
|
||||
spv::Block& block_min_max_head = *builder_->getBuildPoint();
|
||||
spv::Block& block_min_max_min = builder_->makeNewBlock();
|
||||
spv::Block& block_min_max_max = builder_->makeNewBlock();
|
||||
spv::Block& block_min_max_default = builder_->makeNewBlock();
|
||||
spv::Block& block_min_max_merge = builder_->makeNewBlock();
|
||||
builder_->createSelectionMerge(&block_min_max_merge,
|
||||
// Apply blend factors to source and destination first.
|
||||
// Note: Unlike Vulkan's VK_BLEND_OP_MIN/MAX which ignore blend factors,
|
||||
// the Xbox 360 applies blend factors before the min/max operation.
|
||||
// So we apply factors unconditionally, then switch on the equation.
|
||||
spv::Id term_source, term_dest;
|
||||
if (is_alpha) {
|
||||
term_source = FSI_ApplyAlphaBlendFactor(
|
||||
source_alpha_clamped, is_fixed_point, clamp_min_value, clamp_max_value,
|
||||
source_factor, source_alpha_clamped, dest_alpha,
|
||||
constant_alpha_clamped);
|
||||
term_dest = FSI_ApplyAlphaBlendFactor(
|
||||
dest_alpha, is_fixed_point, clamp_min_value, clamp_max_value,
|
||||
dest_factor, source_alpha_clamped, dest_alpha, constant_alpha_clamped);
|
||||
} else {
|
||||
term_source = FSI_ApplyColorBlendFactor(
|
||||
source_color_clamped, is_fixed_point, clamp_min_value, clamp_max_value,
|
||||
source_factor, source_color_clamped, source_alpha_clamped, dest_color,
|
||||
dest_alpha, constant_color_clamped, constant_alpha_clamped);
|
||||
term_dest = FSI_ApplyColorBlendFactor(
|
||||
dest_color, is_fixed_point, clamp_min_value, clamp_max_value,
|
||||
dest_factor, source_color_clamped, source_alpha_clamped, dest_color,
|
||||
dest_alpha, constant_color_clamped, constant_alpha_clamped);
|
||||
}
|
||||
|
||||
// Now switch on the blend equation to combine the factored terms.
|
||||
spv::Block& block_equation_head = *builder_->getBuildPoint();
|
||||
spv::Block& block_equation_add = builder_->makeNewBlock();
|
||||
spv::Block& block_equation_subtract = builder_->makeNewBlock();
|
||||
spv::Block& block_equation_rev_subtract = builder_->makeNewBlock();
|
||||
spv::Block& block_equation_min = builder_->makeNewBlock();
|
||||
spv::Block& block_equation_max = builder_->makeNewBlock();
|
||||
spv::Block& block_equation_merge = builder_->makeNewBlock();
|
||||
builder_->createSelectionMerge(&block_equation_merge,
|
||||
spv::SelectionControlDontFlattenMask);
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> min_max_switch_op =
|
||||
std::unique_ptr<spv::Instruction> equation_switch_op =
|
||||
std::make_unique<spv::Instruction>(spv::OpSwitch);
|
||||
min_max_switch_op->addIdOperand(equation);
|
||||
min_max_switch_op->addIdOperand(block_min_max_default.getId());
|
||||
min_max_switch_op->addImmediateOperand(int32_t(xenos::BlendOp::kMin));
|
||||
min_max_switch_op->addIdOperand(block_min_max_min.getId());
|
||||
min_max_switch_op->addImmediateOperand(int32_t(xenos::BlendOp::kMax));
|
||||
min_max_switch_op->addIdOperand(block_min_max_max.getId());
|
||||
builder_->getBuildPoint()->addInstruction(std::move(min_max_switch_op));
|
||||
equation_switch_op->addIdOperand(equation);
|
||||
// Make addition the default.
|
||||
equation_switch_op->addIdOperand(block_equation_add.getId());
|
||||
equation_switch_op->addImmediateOperand(int32_t(xenos::BlendOp::kSubtract));
|
||||
equation_switch_op->addIdOperand(block_equation_subtract.getId());
|
||||
equation_switch_op->addImmediateOperand(
|
||||
int32_t(xenos::BlendOp::kRevSubtract));
|
||||
equation_switch_op->addIdOperand(block_equation_rev_subtract.getId());
|
||||
equation_switch_op->addImmediateOperand(int32_t(xenos::BlendOp::kMin));
|
||||
equation_switch_op->addIdOperand(block_equation_min.getId());
|
||||
equation_switch_op->addImmediateOperand(int32_t(xenos::BlendOp::kMax));
|
||||
equation_switch_op->addIdOperand(block_equation_max.getId());
|
||||
builder_->getBuildPoint()->addInstruction(std::move(equation_switch_op));
|
||||
}
|
||||
block_min_max_default.addPredecessor(&block_min_max_head);
|
||||
block_min_max_min.addPredecessor(&block_min_max_head);
|
||||
block_min_max_max.addPredecessor(&block_min_max_head);
|
||||
block_equation_add.addPredecessor(&block_equation_head);
|
||||
block_equation_subtract.addPredecessor(&block_equation_head);
|
||||
block_equation_rev_subtract.addPredecessor(&block_equation_head);
|
||||
block_equation_min.addPredecessor(&block_equation_head);
|
||||
block_equation_max.addPredecessor(&block_equation_head);
|
||||
|
||||
// Addition case (default).
|
||||
builder_->setBuildPoint(&block_equation_add);
|
||||
spv::Id result_add = builder_->createNoContractionBinOp(
|
||||
spv::OpFAdd, value_type, term_source, term_dest);
|
||||
builder_->createBranch(&block_equation_merge);
|
||||
|
||||
// Subtraction case.
|
||||
builder_->setBuildPoint(&block_equation_subtract);
|
||||
spv::Id result_subtract = builder_->createNoContractionBinOp(
|
||||
spv::OpFSub, value_type, term_source, term_dest);
|
||||
builder_->createBranch(&block_equation_merge);
|
||||
|
||||
// Reverse subtraction case.
|
||||
builder_->setBuildPoint(&block_equation_rev_subtract);
|
||||
spv::Id result_rev_subtract = builder_->createNoContractionBinOp(
|
||||
spv::OpFSub, value_type, term_dest, term_source);
|
||||
builder_->createBranch(&block_equation_merge);
|
||||
|
||||
// Min case.
|
||||
builder_->setBuildPoint(&block_min_max_min);
|
||||
spv::Id result_min = builder_->createBinBuiltinCall(
|
||||
value_type, ext_inst_glsl_std_450_, GLSLstd450FMin,
|
||||
is_alpha ? source_alpha_clamped : source_color_clamped,
|
||||
is_alpha ? dest_alpha : dest_color);
|
||||
builder_->createBranch(&block_min_max_merge);
|
||||
builder_->setBuildPoint(&block_equation_min);
|
||||
spv::Id result_min =
|
||||
builder_->createBinBuiltinCall(value_type, ext_inst_glsl_std_450_,
|
||||
GLSLstd450FMin, term_source, term_dest);
|
||||
builder_->createBranch(&block_equation_merge);
|
||||
|
||||
// Max case.
|
||||
builder_->setBuildPoint(&block_min_max_max);
|
||||
spv::Id result_max = builder_->createBinBuiltinCall(
|
||||
value_type, ext_inst_glsl_std_450_, GLSLstd450FMax,
|
||||
is_alpha ? source_alpha_clamped : source_color_clamped,
|
||||
is_alpha ? dest_alpha : dest_color);
|
||||
builder_->createBranch(&block_min_max_merge);
|
||||
builder_->setBuildPoint(&block_equation_max);
|
||||
spv::Id result_max =
|
||||
builder_->createBinBuiltinCall(value_type, ext_inst_glsl_std_450_,
|
||||
GLSLstd450FMax, term_source, term_dest);
|
||||
builder_->createBranch(&block_equation_merge);
|
||||
|
||||
// Blending with factors.
|
||||
spv::Id result_factors;
|
||||
{
|
||||
builder_->setBuildPoint(&block_min_max_default);
|
||||
|
||||
spv::Id term_source, term_dest;
|
||||
if (is_alpha) {
|
||||
term_source = FSI_ApplyAlphaBlendFactor(
|
||||
source_alpha_clamped, is_fixed_point, clamp_min_value,
|
||||
clamp_max_value, source_factor, source_alpha_clamped, dest_alpha,
|
||||
constant_alpha_clamped);
|
||||
term_dest = FSI_ApplyAlphaBlendFactor(dest_alpha, is_fixed_point,
|
||||
clamp_min_value, clamp_max_value,
|
||||
dest_factor, source_alpha_clamped,
|
||||
dest_alpha, constant_alpha_clamped);
|
||||
} else {
|
||||
term_source = FSI_ApplyColorBlendFactor(
|
||||
source_color_clamped, is_fixed_point, clamp_min_value,
|
||||
clamp_max_value, source_factor, source_color_clamped,
|
||||
source_alpha_clamped, dest_color, dest_alpha, constant_color_clamped,
|
||||
constant_alpha_clamped);
|
||||
term_dest = FSI_ApplyColorBlendFactor(
|
||||
dest_color, is_fixed_point, clamp_min_value, clamp_max_value,
|
||||
dest_factor, source_color_clamped, source_alpha_clamped, dest_color,
|
||||
dest_alpha, constant_color_clamped, constant_alpha_clamped);
|
||||
}
|
||||
|
||||
spv::Block& block_signs_head = *builder_->getBuildPoint();
|
||||
spv::Block& block_signs_add = builder_->makeNewBlock();
|
||||
spv::Block& block_signs_subtract = builder_->makeNewBlock();
|
||||
spv::Block& block_signs_reverse_subtract = builder_->makeNewBlock();
|
||||
spv::Block& block_signs_merge = builder_->makeNewBlock();
|
||||
builder_->createSelectionMerge(&block_signs_merge,
|
||||
spv::SelectionControlDontFlattenMask);
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> signs_switch_op =
|
||||
std::make_unique<spv::Instruction>(spv::OpSwitch);
|
||||
signs_switch_op->addIdOperand(equation);
|
||||
// Make addition the default.
|
||||
signs_switch_op->addIdOperand(block_signs_add.getId());
|
||||
signs_switch_op->addImmediateOperand(int32_t(xenos::BlendOp::kSubtract));
|
||||
signs_switch_op->addIdOperand(block_signs_subtract.getId());
|
||||
signs_switch_op->addImmediateOperand(
|
||||
int32_t(xenos::BlendOp::kRevSubtract));
|
||||
signs_switch_op->addIdOperand(block_signs_reverse_subtract.getId());
|
||||
builder_->getBuildPoint()->addInstruction(std::move(signs_switch_op));
|
||||
}
|
||||
block_signs_add.addPredecessor(&block_signs_head);
|
||||
block_signs_subtract.addPredecessor(&block_signs_head);
|
||||
block_signs_reverse_subtract.addPredecessor(&block_signs_head);
|
||||
|
||||
// Addition case.
|
||||
builder_->setBuildPoint(&block_signs_add);
|
||||
spv::Id result_add = builder_->createNoContractionBinOp(
|
||||
spv::OpFAdd, value_type, term_source, term_dest);
|
||||
builder_->createBranch(&block_signs_merge);
|
||||
|
||||
// Subtraction case.
|
||||
builder_->setBuildPoint(&block_signs_subtract);
|
||||
spv::Id result_subtract = builder_->createNoContractionBinOp(
|
||||
spv::OpFSub, value_type, term_source, term_dest);
|
||||
builder_->createBranch(&block_signs_merge);
|
||||
|
||||
// Reverse subtraction case.
|
||||
builder_->setBuildPoint(&block_signs_reverse_subtract);
|
||||
spv::Id result_reverse_subtract = builder_->createNoContractionBinOp(
|
||||
spv::OpFSub, value_type, term_dest, term_source);
|
||||
builder_->createBranch(&block_signs_merge);
|
||||
|
||||
// Selection between the signs involved in the addition.
|
||||
builder_->setBuildPoint(&block_signs_merge);
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(2 * 3);
|
||||
id_vector_temp_.push_back(result_add);
|
||||
id_vector_temp_.push_back(block_signs_add.getId());
|
||||
id_vector_temp_.push_back(result_subtract);
|
||||
id_vector_temp_.push_back(block_signs_subtract.getId());
|
||||
id_vector_temp_.push_back(result_reverse_subtract);
|
||||
id_vector_temp_.push_back(block_signs_reverse_subtract.getId());
|
||||
result_factors =
|
||||
builder_->createOp(spv::OpPhi, value_type, id_vector_temp_);
|
||||
builder_->createBranch(&block_min_max_merge);
|
||||
}
|
||||
// Get the latest block for blending with factors after all the control flow.
|
||||
spv::Block& block_min_max_default_end = *builder_->getBuildPoint();
|
||||
|
||||
builder_->setBuildPoint(&block_min_max_merge);
|
||||
// Choose out of min, max, and blending with factors.
|
||||
// Merge and create phi for the result.
|
||||
builder_->setBuildPoint(&block_equation_merge);
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(2 * 3);
|
||||
id_vector_temp_.push_back(result_add);
|
||||
id_vector_temp_.push_back(block_equation_add.getId());
|
||||
id_vector_temp_.push_back(result_subtract);
|
||||
id_vector_temp_.push_back(block_equation_subtract.getId());
|
||||
id_vector_temp_.push_back(result_rev_subtract);
|
||||
id_vector_temp_.push_back(block_equation_rev_subtract.getId());
|
||||
id_vector_temp_.push_back(result_min);
|
||||
id_vector_temp_.push_back(block_min_max_min.getId());
|
||||
id_vector_temp_.push_back(block_equation_min.getId());
|
||||
id_vector_temp_.push_back(result_max);
|
||||
id_vector_temp_.push_back(block_min_max_max.getId());
|
||||
id_vector_temp_.push_back(result_factors);
|
||||
id_vector_temp_.push_back(block_min_max_default_end.getId());
|
||||
return builder_->createOp(spv::OpPhi, value_type, id_vector_temp_);
|
||||
id_vector_temp_.push_back(block_equation_max.getId());
|
||||
spv::Id result_unclamped =
|
||||
builder_->createOp(spv::OpPhi, value_type, id_vector_temp_);
|
||||
|
||||
return FSI_FlushNaNClampAndInBlending(result_unclamped, is_fixed_point,
|
||||
clamp_min_value, clamp_max_value);
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::FSI_AlphaToMaskSample(
|
||||
|
||||
@@ -344,6 +344,38 @@ VulkanPipelineCache::GetCurrentPixelShaderModification(
|
||||
} else {
|
||||
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
|
||||
}
|
||||
|
||||
// Check if MIN/MAX blend is used with non-trivial source factors.
|
||||
// Vulkan/D3D12 fixed-function blend ignores factors for MIN/MAX, but
|
||||
// Xbox 360 applies them. If the destination factor is ONE (or ZERO), we can
|
||||
// pre-multiply the shader output by the source factor to emulate this.
|
||||
// Only RT0 is supported for now.
|
||||
modification.pixel.rt0_blend_rgb_factor_for_premult =
|
||||
xenos::BlendFactor::kOne;
|
||||
modification.pixel.rt0_blend_a_factor_for_premult =
|
||||
xenos::BlendFactor::kOne;
|
||||
|
||||
if (shader.writes_color_target(0)) {
|
||||
auto blend_control = regs.Get<reg::RB_BLENDCONTROL>(
|
||||
reg::RB_BLENDCONTROL::rt_register_indices[0]);
|
||||
|
||||
// Pre-multiply by kSrcAlpha for MIN/MAX blend ops when dstFactor is ONE.
|
||||
if ((blend_control.color_comb_fcn == xenos::BlendOp::kMin ||
|
||||
blend_control.color_comb_fcn == xenos::BlendOp::kMax) &&
|
||||
blend_control.color_srcblend == xenos::BlendFactor::kSrcAlpha &&
|
||||
blend_control.color_destblend == xenos::BlendFactor::kOne) {
|
||||
modification.pixel.rt0_blend_rgb_factor_for_premult =
|
||||
xenos::BlendFactor::kSrcAlpha;
|
||||
}
|
||||
|
||||
if ((blend_control.alpha_comb_fcn == xenos::BlendOp::kMin ||
|
||||
blend_control.alpha_comb_fcn == xenos::BlendOp::kMax) &&
|
||||
blend_control.alpha_srcblend == xenos::BlendFactor::kSrcAlpha &&
|
||||
blend_control.alpha_destblend == xenos::BlendFactor::kOne) {
|
||||
modification.pixel.rt0_blend_a_factor_for_premult =
|
||||
xenos::BlendFactor::kSrcAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modification;
|
||||
@@ -2383,6 +2415,16 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
VK_BLEND_OP_ADD,
|
||||
VK_BLEND_OP_ADD,
|
||||
VK_BLEND_OP_ADD};
|
||||
// Check if the shader pre-multiplies by blend factors for MIN/MAX.
|
||||
SpirvShaderTranslator::Modification pixel_shader_modification(
|
||||
description.pixel_shader_modification);
|
||||
bool rt0_rgb_premult =
|
||||
pixel_shader_modification.pixel.rt0_blend_rgb_factor_for_premult !=
|
||||
xenos::BlendFactor::kOne;
|
||||
bool rt0_a_premult =
|
||||
pixel_shader_modification.pixel.rt0_blend_a_factor_for_premult !=
|
||||
xenos::BlendFactor::kOne;
|
||||
|
||||
uint32_t color_rts_remaining = color_rts_used;
|
||||
uint32_t color_rt_index;
|
||||
while (xe::bit_scan_forward(color_rts_remaining, &color_rt_index)) {
|
||||
@@ -2410,6 +2452,18 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
||||
kBlendFactorMap[uint32_t(color_rt.dst_alpha_blend_factor)];
|
||||
color_blend_attachment.alphaBlendOp =
|
||||
kBlendOpMap[uint32_t(color_rt.alpha_blend_op)];
|
||||
|
||||
// If the shader pre-multiplies by the source blend factor for RT0
|
||||
// MIN/MAX, set the pipeline source factor to ONE since it's already
|
||||
// applied in the shader.
|
||||
if (color_rt_index == 0) {
|
||||
if (rt0_rgb_premult) {
|
||||
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
}
|
||||
if (rt0_a_premult) {
|
||||
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
color_blend_attachment.colorWriteMask =
|
||||
VkColorComponentFlags(color_rt.color_write_mask);
|
||||
|
||||
Reference in New Issue
Block a user