mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
gpu: fix separate alpha blending on swizzled render targets
Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,15 @@ BlendMappingSupport ClassifyBlendMapping(const HW::BlendControl&
|
|||||||
if (mapping.Map(3) == 3u) {
|
if (mapping.Map(3) == 3u) {
|
||||||
return BlendMappingSupport::Direct;
|
return BlendMappingSupport::Direct;
|
||||||
}
|
}
|
||||||
|
if (blend.separate_alpha_blend &&
|
||||||
|
blend.color_srcblend == static_cast<uint8_t>(Prospero::BlendFactor::kSrcAlpha) &&
|
||||||
|
blend.alpha_srcblend == static_cast<uint8_t>(Prospero::BlendFactor::kOne) &&
|
||||||
|
blend.color_destblend == static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrcAlpha) &&
|
||||||
|
blend.alpha_destblend == blend.color_destblend &&
|
||||||
|
blend.color_comb_fcn == static_cast<uint8_t>(Prospero::BlendOp::kAdd) &&
|
||||||
|
blend.alpha_comb_fcn == blend.color_comb_fcn) {
|
||||||
|
return BlendMappingSupport::SourceAlphaOne;
|
||||||
|
}
|
||||||
// Moving alpha requires the same equation for all channels.
|
// Moving alpha requires the same equation for all channels.
|
||||||
if (blend.separate_alpha_blend && (blend.alpha_srcblend != blend.color_srcblend ||
|
if (blend.separate_alpha_blend && (blend.alpha_srcblend != blend.color_srcblend ||
|
||||||
blend.alpha_destblend != blend.color_destblend ||
|
blend.alpha_destblend != blend.color_destblend ||
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct ColorComponentMapping;
|
|||||||
enum class BlendMappingSupport {
|
enum class BlendMappingSupport {
|
||||||
Direct,
|
Direct,
|
||||||
SourceAlpha, // Requires logical alpha in the second blend source.
|
SourceAlpha, // Requires logical alpha in the second blend source.
|
||||||
|
SourceAlphaOne, // Source alpha for RGB, one for alpha, with a shared destination equation.
|
||||||
Unsupported,
|
Unsupported,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,16 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
uint8_t RemapSourceAlphaFactor(uint8_t factor) {
|
||||||
|
switch (static_cast<Prospero::BlendFactor>(factor)) {
|
||||||
|
case Prospero::BlendFactor::kSrcAlpha:
|
||||||
|
return static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Alpha);
|
||||||
|
case Prospero::BlendFactor::kOneMinusSrcAlpha:
|
||||||
|
return static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrc1Alpha);
|
||||||
|
default: return factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vk::PolygonMode ResolvePolygonMode(const HW::ModeControl& mode, bool cull_front, bool cull_back) {
|
vk::PolygonMode ResolvePolygonMode(const HW::ModeControl& mode, bool cull_front, bool cull_back) {
|
||||||
// CxPrimitiveSetup::PolygonMode disables both per-face modes when it is zero.
|
// CxPrimitiveSetup::PolygonMode disables both per-face modes when it is zero.
|
||||||
if (mode.poly_mode == 0) {
|
if (mode.poly_mode == 0) {
|
||||||
@@ -619,14 +629,21 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
|
|||||||
pixel_info.target_output_mode[0] != 0 && pixel_info.target_output_mode[0] != 7 &&
|
pixel_info.target_output_mode[0] != 0 && pixel_info.target_output_mode[0] != 7 &&
|
||||||
std::all_of(std::begin(pixel_info.target_output_mode) + 1,
|
std::all_of(std::begin(pixel_info.target_output_mode) + 1,
|
||||||
std::end(pixel_info.target_output_mode),
|
std::end(pixel_info.target_output_mode),
|
||||||
[](uint8_t mode) { return mode == 0; }) &&
|
[](uint8_t mode) { return mode == 0; })) {
|
||||||
ClassifyBlendMapping(blend, pixel_info.target_export_mapping[0]) ==
|
switch (ClassifyBlendMapping(blend, pixel_info.target_export_mapping[0])) {
|
||||||
BlendMappingSupport::SourceAlpha) {
|
case BlendMappingSupport::SourceAlpha:
|
||||||
// Preserve logical alpha when the export mapping moves it.
|
pixel_info.alpha_blend_source = ShaderAlphaBlendSource::SourceAlpha;
|
||||||
pixel_info.alpha_blend_source_remap = true;
|
break;
|
||||||
pixel_info.dual_source_blending = true;
|
case BlendMappingSupport::SourceAlphaOne:
|
||||||
pixel_info.target_output_mode[1] = pixel_info.target_output_mode[0];
|
pixel_info.alpha_blend_source = ShaderAlphaBlendSource::SourceAlphaOne;
|
||||||
pixel_info.target_export_mapping[1] = {};
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (pixel_info.alpha_blend_source != ShaderAlphaBlendSource::None) {
|
||||||
|
pixel_info.dual_source_blending = true;
|
||||||
|
pixel_info.target_output_mode[1] = pixel_info.target_output_mode[0];
|
||||||
|
pixel_info.target_export_mapping[1] = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (context.GetClipControl().clip_disable) {
|
if (context.GetClipControl().clip_disable) {
|
||||||
@@ -729,10 +746,12 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
|||||||
}
|
}
|
||||||
const auto& rt = ctx.GetRenderTarget(colors[i].target_slot);
|
const auto& rt = ctx.GetRenderTarget(colors[i].target_slot);
|
||||||
const auto& bc = ctx.GetBlendControl(colors[i].target_slot);
|
const auto& bc = ctx.GetBlendControl(colors[i].target_slot);
|
||||||
const bool alpha_remap =
|
auto alpha_source = ShaderAlphaBlendSource::None;
|
||||||
slot == 0 && ps_input_info != nullptr && ps_input_info->alpha_blend_source_remap;
|
if (slot == 0 && ps_input_info != nullptr) {
|
||||||
|
alpha_source = ps_input_info->alpha_blend_source;
|
||||||
|
}
|
||||||
static_params.blend_enable[slot] = bc.enable && !rt.info.blend_bypass;
|
static_params.blend_enable[slot] = bc.enable && !rt.info.blend_bypass;
|
||||||
if (static_params.blend_enable[slot] && !alpha_remap &&
|
if (static_params.blend_enable[slot] && alpha_source == ShaderAlphaBlendSource::None &&
|
||||||
ClassifyBlendMapping(bc, colors[i].export_mapping) != BlendMappingSupport::Direct) {
|
ClassifyBlendMapping(bc, colors[i].export_mapping) != BlendMappingSupport::Direct) {
|
||||||
static_params.blend_enable[slot] = false;
|
static_params.blend_enable[slot] = false;
|
||||||
static std::atomic_bool warned = false;
|
static std::atomic_bool warned = false;
|
||||||
@@ -744,18 +763,34 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
|||||||
bc.alpha_srcblend, bc.alpha_destblend, bc.separate_alpha_blend ? 1 : 0));
|
bc.alpha_srcblend, bc.alpha_destblend, bc.separate_alpha_blend ? 1 : 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (alpha_remap) {
|
|
||||||
static_params.blend_alpha_source_remap = true;
|
|
||||||
}
|
|
||||||
if (static_params.blend_enable[slot]) {
|
if (static_params.blend_enable[slot]) {
|
||||||
static_params.color_srcblend[slot] = bc.color_srcblend;
|
auto blend = bc;
|
||||||
static_params.color_comb_fcn[slot] = bc.color_comb_fcn;
|
switch (alpha_source) {
|
||||||
static_params.color_destblend[slot] = bc.color_destblend;
|
case ShaderAlphaBlendSource::SourceAlpha:
|
||||||
static_params.separate_alpha_blend[slot] = bc.separate_alpha_blend;
|
blend.color_srcblend = RemapSourceAlphaFactor(blend.color_srcblend);
|
||||||
if (bc.separate_alpha_blend) {
|
blend.color_destblend = RemapSourceAlphaFactor(blend.color_destblend);
|
||||||
static_params.alpha_srcblend[slot] = bc.alpha_srcblend;
|
if (blend.separate_alpha_blend) {
|
||||||
static_params.alpha_comb_fcn[slot] = bc.alpha_comb_fcn;
|
blend.alpha_srcblend = RemapSourceAlphaFactor(blend.alpha_srcblend);
|
||||||
static_params.alpha_destblend[slot] = bc.alpha_destblend;
|
blend.alpha_destblend = RemapSourceAlphaFactor(blend.alpha_destblend);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ShaderAlphaBlendSource::SourceAlphaOne:
|
||||||
|
// The second source carries the mapped source factor; its alpha stays logical Sa.
|
||||||
|
blend.color_srcblend = static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Color);
|
||||||
|
blend.color_destblend =
|
||||||
|
static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrc1Alpha);
|
||||||
|
blend.separate_alpha_blend = false;
|
||||||
|
break;
|
||||||
|
case ShaderAlphaBlendSource::None: break;
|
||||||
|
}
|
||||||
|
static_params.color_srcblend[slot] = blend.color_srcblend;
|
||||||
|
static_params.color_comb_fcn[slot] = blend.color_comb_fcn;
|
||||||
|
static_params.color_destblend[slot] = blend.color_destblend;
|
||||||
|
static_params.separate_alpha_blend[slot] = blend.separate_alpha_blend;
|
||||||
|
if (blend.separate_alpha_blend) {
|
||||||
|
static_params.alpha_srcblend[slot] = blend.alpha_srcblend;
|
||||||
|
static_params.alpha_comb_fcn[slot] = blend.alpha_comb_fcn;
|
||||||
|
static_params.alpha_destblend[slot] = blend.alpha_destblend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ struct PipelineStaticParameters {
|
|||||||
uint8_t alpha_destblend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
uint8_t alpha_destblend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||||
bool separate_alpha_blend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
bool separate_alpha_blend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||||
bool blend_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
bool blend_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||||
bool blend_alpha_source_remap = false;
|
|
||||||
|
|
||||||
bool operator==(const PipelineStaticParameters& other) const noexcept;
|
bool operator==(const PipelineStaticParameters& other) const noexcept;
|
||||||
};
|
};
|
||||||
@@ -65,7 +64,7 @@ struct PipelineStaticParameters {
|
|||||||
static_assert(std::is_trivially_copyable_v<PipelineStaticParameters>);
|
static_assert(std::is_trivially_copyable_v<PipelineStaticParameters>);
|
||||||
static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
||||||
static_assert(alignof(PipelineStaticParameters) == 1);
|
static_assert(alignof(PipelineStaticParameters) == 1);
|
||||||
static_assert(sizeof(PipelineStaticParameters) == 126);
|
static_assert(sizeof(PipelineStaticParameters) == 125);
|
||||||
|
|
||||||
struct PipelineRenderingState {
|
struct PipelineRenderingState {
|
||||||
std::array<vk::Format, RENDER_COLOR_ATTACHMENTS_MAX> color_formats {};
|
std::array<vk::Format, RENDER_COLOR_ATTACHMENTS_MAX> color_formats {};
|
||||||
|
|||||||
@@ -137,17 +137,14 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static vk::BlendFactor GetBlendFactor(uint32_t factor, bool remap_source_alpha) {
|
static vk::BlendFactor GetBlendFactor(uint32_t factor) {
|
||||||
switch (static_cast<Prospero::BlendFactor>(factor)) {
|
switch (static_cast<Prospero::BlendFactor>(factor)) {
|
||||||
case Prospero::BlendFactor::kZero: return vk::BlendFactor::eZero;
|
case Prospero::BlendFactor::kZero: return vk::BlendFactor::eZero;
|
||||||
case Prospero::BlendFactor::kOne: return vk::BlendFactor::eOne;
|
case Prospero::BlendFactor::kOne: return vk::BlendFactor::eOne;
|
||||||
case Prospero::BlendFactor::kSrcColor: return vk::BlendFactor::eSrcColor;
|
case Prospero::BlendFactor::kSrcColor: return vk::BlendFactor::eSrcColor;
|
||||||
case Prospero::BlendFactor::kOneMinusSrcColor: return vk::BlendFactor::eOneMinusSrcColor;
|
case Prospero::BlendFactor::kOneMinusSrcColor: return vk::BlendFactor::eOneMinusSrcColor;
|
||||||
case Prospero::BlendFactor::kSrcAlpha:
|
case Prospero::BlendFactor::kSrcAlpha: return vk::BlendFactor::eSrcAlpha;
|
||||||
return remap_source_alpha ? vk::BlendFactor::eSrc1Color : vk::BlendFactor::eSrcAlpha;
|
case Prospero::BlendFactor::kOneMinusSrcAlpha: return vk::BlendFactor::eOneMinusSrcAlpha;
|
||||||
case Prospero::BlendFactor::kOneMinusSrcAlpha:
|
|
||||||
return remap_source_alpha ? vk::BlendFactor::eOneMinusSrc1Color
|
|
||||||
: vk::BlendFactor::eOneMinusSrcAlpha;
|
|
||||||
case Prospero::BlendFactor::kDstAlpha: return vk::BlendFactor::eDstAlpha;
|
case Prospero::BlendFactor::kDstAlpha: return vk::BlendFactor::eDstAlpha;
|
||||||
case Prospero::BlendFactor::kOneMinusDstAlpha: return vk::BlendFactor::eOneMinusDstAlpha;
|
case Prospero::BlendFactor::kOneMinusDstAlpha: return vk::BlendFactor::eOneMinusDstAlpha;
|
||||||
case Prospero::BlendFactor::kDstColor: return vk::BlendFactor::eDstColor;
|
case Prospero::BlendFactor::kDstColor: return vk::BlendFactor::eDstColor;
|
||||||
@@ -397,20 +394,18 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p
|
|||||||
color_blend_attachment[i].colorWriteMask =
|
color_blend_attachment[i].colorWriteMask =
|
||||||
vk::ColorComponentFlags {static_params.color_mask[i]};
|
vk::ColorComponentFlags {static_params.color_mask[i]};
|
||||||
color_blend_attachment[i].blendEnable = static_params.blend_enable[i] ? VK_TRUE : VK_FALSE;
|
color_blend_attachment[i].blendEnable = static_params.blend_enable[i] ? VK_TRUE : VK_FALSE;
|
||||||
// Only target 0 can use the second blend source carrying logical alpha.
|
|
||||||
const bool remap_source_alpha = i == 0 && static_params.blend_alpha_source_remap;
|
|
||||||
color_blend_attachment[i].srcColorBlendFactor =
|
color_blend_attachment[i].srcColorBlendFactor =
|
||||||
GetBlendFactor(static_params.color_srcblend[i], remap_source_alpha);
|
GetBlendFactor(static_params.color_srcblend[i]);
|
||||||
color_blend_attachment[i].dstColorBlendFactor =
|
color_blend_attachment[i].dstColorBlendFactor =
|
||||||
GetBlendFactor(static_params.color_destblend[i], remap_source_alpha);
|
GetBlendFactor(static_params.color_destblend[i]);
|
||||||
color_blend_attachment[i].colorBlendOp = GetBlendOp(static_params.color_comb_fcn[i]);
|
color_blend_attachment[i].colorBlendOp = GetBlendOp(static_params.color_comb_fcn[i]);
|
||||||
color_blend_attachment[i].srcAlphaBlendFactor =
|
color_blend_attachment[i].srcAlphaBlendFactor =
|
||||||
(static_params.separate_alpha_blend[i]
|
(static_params.separate_alpha_blend[i]
|
||||||
? GetBlendFactor(static_params.alpha_srcblend[i], remap_source_alpha)
|
? GetBlendFactor(static_params.alpha_srcblend[i])
|
||||||
: color_blend_attachment[i].srcColorBlendFactor);
|
: color_blend_attachment[i].srcColorBlendFactor);
|
||||||
color_blend_attachment[i].dstAlphaBlendFactor =
|
color_blend_attachment[i].dstAlphaBlendFactor =
|
||||||
(static_params.separate_alpha_blend[i]
|
(static_params.separate_alpha_blend[i]
|
||||||
? GetBlendFactor(static_params.alpha_destblend[i], remap_source_alpha)
|
? GetBlendFactor(static_params.alpha_destblend[i])
|
||||||
: color_blend_attachment[i].dstColorBlendFactor);
|
: color_blend_attachment[i].dstColorBlendFactor);
|
||||||
color_blend_attachment[i].alphaBlendOp =
|
color_blend_attachment[i].alphaBlendOp =
|
||||||
(static_params.separate_alpha_blend[i] ? GetBlendOp(static_params.alpha_comb_fcn[i])
|
(static_params.separate_alpha_blend[i] ? GetBlendOp(static_params.alpha_comb_fcn[i])
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
}
|
}
|
||||||
// Skip dormant color exports after their valid mask; MRT1 is reserved for logical alpha.
|
// Skip dormant color exports after their valid mask; MRT1 is reserved for logical alpha.
|
||||||
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
||||||
exp.index != 0 && state.input_info.pixel->alpha_blend_source_remap) {
|
exp.index != 0 && state.input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EmitIfCondition(state, exec, [&]() {
|
EmitIfCondition(state, exec, [&]() {
|
||||||
@@ -503,14 +503,27 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto vector_type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
const auto vector_type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
||||||
auto value = ExportVector(ctx, data, exp, uint_output);
|
auto value = ExportVector(ctx, data, exp, uint_output);
|
||||||
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
||||||
exp.index == 0 && !uint_output && state.input_info.pixel->alpha_blend_source_remap) {
|
exp.index == 0 && !uint_output &&
|
||||||
|
state.input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None) {
|
||||||
// Broadcast logical alpha before swizzling the primary output.
|
// Broadcast logical alpha before swizzling the primary output.
|
||||||
const auto blend_output =
|
const auto blend_output =
|
||||||
OutputVariableForExport(state, {.kind = IR::ExportTargetKind::Mrt, .index = 1});
|
OutputVariableForExport(state, {.kind = IR::ExportTargetKind::Mrt, .index = 1});
|
||||||
if (blend_output != 0) {
|
if (blend_output != 0) {
|
||||||
const auto alpha = state.builder.AllocateId();
|
auto alpha = state.builder.AllocateId();
|
||||||
state.builder.AddFunction(spv::OpVectorShuffle, vector_type, alpha, value, value,
|
state.builder.AddFunction(spv::OpVectorShuffle, vector_type, alpha, value, value,
|
||||||
3u, 3u, 3u, 3u);
|
3u, 3u, 3u, 3u);
|
||||||
|
if (state.input_info.pixel->alpha_blend_source ==
|
||||||
|
ShaderAlphaBlendSource::SourceAlphaOne) {
|
||||||
|
const auto mapping = state.input_info.pixel->target_export_mapping[0];
|
||||||
|
for (uint32_t component = 0; component < 4; component++) {
|
||||||
|
if (mapping.Map(component) != 3u) continue;
|
||||||
|
const auto factors = state.builder.AllocateId();
|
||||||
|
state.builder.AddFunction(spv::OpCompositeInsert, vector_type, factors,
|
||||||
|
ConstantF32Value(state, 1.0f), alpha, component);
|
||||||
|
alpha = factors;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
state.builder.AddFunction(spv::OpStore, blend_output, alpha);
|
state.builder.AddFunction(spv::OpStore, blend_output, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
|
|||||||
|
|
||||||
void CollectOutputs(const Program& program, ShaderStageInputInfo input_info, ShaderInfo& info) {
|
void CollectOutputs(const Program& program, ShaderStageInputInfo input_info, ShaderInfo& info) {
|
||||||
const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel != nullptr &&
|
const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel != nullptr &&
|
||||||
input_info.pixel->alpha_blend_source_remap;
|
input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None;
|
||||||
for (const auto* block: program.blocks) {
|
for (const auto* block: program.blocks) {
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
if (inst.GetOpcode() != ValueOpcode::SetAttribute) {
|
if (inst.GetOpcode() != ValueOpcode::SetAttribute) {
|
||||||
|
|||||||
@@ -660,7 +660,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
|||||||
key.push_back(static_cast<uint32_t>(info.ps_sample_mask_export_enable));
|
key.push_back(static_cast<uint32_t>(info.ps_sample_mask_export_enable));
|
||||||
key.push_back(static_cast<uint32_t>(info.ps_early_z));
|
key.push_back(static_cast<uint32_t>(info.ps_early_z));
|
||||||
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
|
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
|
||||||
key.push_back(static_cast<uint32_t>(info.alpha_blend_source_remap));
|
key.push_back(static_cast<uint32_t>(info.alpha_blend_source));
|
||||||
key.insert(key.end(), std::begin(info.target_output_mode), std::end(info.target_output_mode));
|
key.insert(key.end(), std::begin(info.target_output_mode), std::end(info.target_output_mode));
|
||||||
for (uint32_t base = 0; base < info.target_export_mapping.size(); base += 4u) {
|
for (uint32_t base = 0; base < info.target_export_mapping.size(); base += 4u) {
|
||||||
uint32_t packed = 0;
|
uint32_t packed = 0;
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ struct ShaderComputeInputInfo: ShaderWorkgroupInputInfo {
|
|||||||
ShaderStageRuntime stage;
|
ShaderStageRuntime stage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ShaderAlphaBlendSource : uint8_t { None, SourceAlpha, SourceAlphaOne };
|
||||||
|
|
||||||
struct ShaderPixelInputInfo {
|
struct ShaderPixelInputInfo {
|
||||||
uint32_t interpolator_settings[32] = {0};
|
uint32_t interpolator_settings[32] = {0};
|
||||||
uint32_t input_num = 0;
|
uint32_t input_num = 0;
|
||||||
@@ -179,8 +181,8 @@ struct ShaderPixelInputInfo {
|
|||||||
bool ps_sample_mask_export_enable = false;
|
bool ps_sample_mask_export_enable = false;
|
||||||
bool ps_sample_shading = false;
|
bool ps_sample_shading = false;
|
||||||
bool dual_source_blending = false;
|
bool dual_source_blending = false;
|
||||||
// Export logical alpha through MRT1 for blending after channel swizzling.
|
// Export logical alpha or per-channel source factors through MRT1 after channel swizzling.
|
||||||
bool alpha_blend_source_remap = false;
|
ShaderAlphaBlendSource alpha_blend_source = ShaderAlphaBlendSource::None;
|
||||||
bool ps_early_z = false;
|
bool ps_early_z = false;
|
||||||
bool ps_execute_on_noop = false;
|
bool ps_execute_on_noop = false;
|
||||||
ShaderStageRuntime stage;
|
ShaderStageRuntime stage;
|
||||||
|
|||||||
@@ -15272,7 +15272,8 @@ public:
|
|||||||
PipelineCache::GraphicsPrograms{{vertex_shader}, pixel_shader});
|
PipelineCache::GraphicsPrograms{{vertex_shader}, pixel_shader});
|
||||||
};
|
};
|
||||||
auto &filled = pipeline(true, 2, 2);
|
auto &filled = pipeline(true, 2, 2);
|
||||||
const auto draw = [&](const PipelineCache::Pipeline &selected, uint32_t vertex_count = 3) {
|
const auto draw = [&](const PipelineCache::Pipeline &selected, uint32_t vertex_count = 3,
|
||||||
|
std::array<u32, 4> clear_value = {}) {
|
||||||
RenderExecutorTestAccess::BindRenderTarget(executor, color.image_id);
|
RenderExecutorTestAccess::BindRenderTarget(executor, color.image_id);
|
||||||
if (depth.image_id) {
|
if (depth.image_id) {
|
||||||
RenderExecutorTestAccess::BindRenderTarget(executor, depth.image_id);
|
RenderExecutorTestAccess::BindRenderTarget(executor, depth.image_id);
|
||||||
@@ -15299,6 +15300,7 @@ public:
|
|||||||
RenderExecutorTestAccess::CommitBindings(
|
RenderExecutorTestAccess::CommitBindings(
|
||||||
executor, command, selected, bindings.vertex[0], *bindings.pixel);
|
executor, command, selected, bindings.vertex[0], *bindings.pixel);
|
||||||
rendering.color_attachments[0].is_clear = true;
|
rendering.color_attachments[0].is_clear = true;
|
||||||
|
rendering.color_attachments[0].clear_value = clear_value;
|
||||||
command.BeginRendering(rendering);
|
command.BeginRendering(rendering);
|
||||||
auto cmd = command.Handle();
|
auto cmd = command.Handle();
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, selected.pipeline);
|
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, selected.pipeline);
|
||||||
@@ -15642,6 +15644,68 @@ public:
|
|||||||
wave_ids[0] != wave_ids[1] && wave_keys[0] != wave_keys[1],
|
wave_ids[0] != wave_ids[1] && wave_keys[0] != wave_keys[1],
|
||||||
"wave32 and wave64 pixel programs shared a cache key");
|
"wave32 and wave64 pixel programs shared a cache key");
|
||||||
|
|
||||||
|
// Reversed RGBA targets must use logical Sa for RGB, with a separate
|
||||||
|
// unit source factor when alpha accumulates as Sa + Da * (1 - Sa).
|
||||||
|
static const auto blend_pixel = [] {
|
||||||
|
std::vector<u32> code;
|
||||||
|
constexpr std::array<float, 4> source{0.8f, 0.6f, 0.4f, 0.5f};
|
||||||
|
for (u32 component = 0; component < source.size(); component++) {
|
||||||
|
AppendVMovLiteral(&code, component, std::bit_cast<u32>(source[component]));
|
||||||
|
}
|
||||||
|
code.push_back(EncodeExp0(0, 0xf));
|
||||||
|
code.push_back(EncodeExp1(0, 1, 2, 3));
|
||||||
|
AppendEnd(&code);
|
||||||
|
return code;
|
||||||
|
}();
|
||||||
|
native_pixel_regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(blend_pixel.data());
|
||||||
|
ShaderMapUserData(native_pixel_regs.ps_regs.data_addr,
|
||||||
|
{.type = Prospero::ShaderBinaryType::kPs,
|
||||||
|
.code_size_bytes = static_cast<uint32_t>(blend_pixel.size() * sizeof(u32))});
|
||||||
|
const auto saved_blend = registers.GetBlendControl(0);
|
||||||
|
const auto saved_target_info = registers.GetRenderTarget(0).info;
|
||||||
|
const auto saved_output_mode = registers.GetShaderRegisters().target_output_mode[0];
|
||||||
|
registers.SetTargetOutputMode(0, 4);
|
||||||
|
auto blend_target_info = saved_target_info;
|
||||||
|
blend_target_info.blend_bypass = false;
|
||||||
|
registers.SetColorInfo(0, blend_target_info);
|
||||||
|
export_mapping[0] = color.export_mapping = Prospero::ColorMappingAbgr;
|
||||||
|
HW::BlendControl alpha_blend{};
|
||||||
|
alpha_blend.enable = true;
|
||||||
|
alpha_blend.color_srcblend = static_cast<uint8_t>(Prospero::BlendFactor::kSrcAlpha);
|
||||||
|
alpha_blend.alpha_srcblend = static_cast<uint8_t>(Prospero::BlendFactor::kOne);
|
||||||
|
alpha_blend.color_destblend = alpha_blend.alpha_destblend =
|
||||||
|
static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrcAlpha);
|
||||||
|
for (const bool separate : {false, true}) {
|
||||||
|
alpha_blend.separate_alpha_blend = separate;
|
||||||
|
registers.SetBlendControl(0, alpha_blend);
|
||||||
|
const auto programs = context.GetPipelineCache().GetGraphicsPrograms(
|
||||||
|
native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(),
|
||||||
|
registers, user_config, export_mapping, true, native_vertex_info, pixel);
|
||||||
|
const auto mode = separate ? ShaderAlphaBlendSource::SourceAlphaOne
|
||||||
|
: ShaderAlphaBlendSource::SourceAlpha;
|
||||||
|
Require(name, "logical alpha blend program", pixel.alpha_blend_source == mode &&
|
||||||
|
pixel.dual_source_blending,
|
||||||
|
"the separate alpha equation did not reach the compiled pixel program");
|
||||||
|
vertex_shader = programs.vertex[0];
|
||||||
|
pixel_shader = programs.pixel;
|
||||||
|
vertex = native_vertex_info[0];
|
||||||
|
constexpr std::array<float, 4> destination{0.3f, 0.3f, 0.1f, 0.2f};
|
||||||
|
draw(pipeline(true, 2, 2), 3, std::bit_cast<std::array<u32, 4>>(destination));
|
||||||
|
const auto blend_pixels = read_color();
|
||||||
|
const std::array<float, 4> expected{separate ? 0.65f : 0.4f, 0.35f, 0.35f, 0.5f};
|
||||||
|
for (size_t component = 0; component < blend_pixels.size(); component++) {
|
||||||
|
Require(name, "logical alpha blend readback",
|
||||||
|
std::abs(std::bit_cast<float>(blend_pixels[component]) -
|
||||||
|
expected[component % 4]) < 0.00001f,
|
||||||
|
"reversed blending changed logical RGB or the separate alpha equation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
native_pixel_regs.ps_regs.data_addr = pixel_address;
|
||||||
|
export_mapping[0] = color.export_mapping = {};
|
||||||
|
registers.SetBlendControl(0, saved_blend);
|
||||||
|
registers.SetColorInfo(0, saved_target_info);
|
||||||
|
registers.SetTargetOutputMode(0, saved_output_mode);
|
||||||
|
|
||||||
// Captured Playroom strips contain the fullscreen triangle followed by an
|
// Captured Playroom strips contain the fullscreen triangle followed by an
|
||||||
// out-of-bounds fetch exporting (0,0,0,0). Additive color reveals any extra triangle.
|
// out-of-bounds fetch exporting (0,0,0,0). Additive color reveals any extra triangle.
|
||||||
static constexpr std::array<std::array<u32, 4>, 4> fourth_positions{{
|
static constexpr std::array<std::array<u32, 4>, 4> fourth_positions{{
|
||||||
|
|||||||
+84
-39
@@ -12851,6 +12851,28 @@ void TestBlendMappingClassification() {
|
|||||||
blend.color_srcblend = static_cast<uint8_t>(Factor::kDstAlpha);
|
blend.color_srcblend = static_cast<uint8_t>(Factor::kDstAlpha);
|
||||||
Check(classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
Check(classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
||||||
"destination alpha incorrectly used the physical alpha channel");
|
"destination alpha incorrectly used the physical alpha channel");
|
||||||
|
|
||||||
|
blend.separate_alpha_blend = true;
|
||||||
|
blend.color_srcblend = static_cast<uint8_t>(Factor::kSrcAlpha);
|
||||||
|
blend.alpha_srcblend = static_cast<uint8_t>(Factor::kOne);
|
||||||
|
blend.color_destblend = blend.alpha_destblend =
|
||||||
|
static_cast<uint8_t>(Factor::kOneMinusSrcAlpha);
|
||||||
|
Check(classify(Prospero::ColorMappingAbgr) == Support::SourceAlphaOne,
|
||||||
|
"separate alpha accumulation did not preserve its unit source factor");
|
||||||
|
const auto exact = blend;
|
||||||
|
for (uint32_t changed = 0; changed < 6; changed++) {
|
||||||
|
blend = exact;
|
||||||
|
switch (changed) {
|
||||||
|
case 0: blend.color_srcblend = static_cast<uint8_t>(Factor::kZero); break;
|
||||||
|
case 1: blend.color_destblend = static_cast<uint8_t>(Factor::kOne); break;
|
||||||
|
case 2: blend.alpha_srcblend = static_cast<uint8_t>(Factor::kZero); break;
|
||||||
|
case 3: blend.alpha_destblend = static_cast<uint8_t>(Factor::kOne); break;
|
||||||
|
case 4: blend.color_comb_fcn = static_cast<uint8_t>(Prospero::BlendOp::kSubtract); break;
|
||||||
|
case 5: blend.alpha_comb_fcn = static_cast<uint8_t>(Prospero::BlendOp::kSubtract); break;
|
||||||
|
}
|
||||||
|
Check(classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
||||||
|
"separate alpha lowering accepted a different blend equation");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestLogicalAlphaBlendExport() {
|
void TestLogicalAlphaBlendExport() {
|
||||||
@@ -12860,58 +12882,81 @@ void TestLogicalAlphaBlendExport() {
|
|||||||
const auto ordinary_key = MakeStageStaticKey(pixel);
|
const auto ordinary_key = MakeStageStaticKey(pixel);
|
||||||
pixel.dual_source_blending = true;
|
pixel.dual_source_blending = true;
|
||||||
const auto guest_key = MakeStageStaticKey(pixel);
|
const auto guest_key = MakeStageStaticKey(pixel);
|
||||||
pixel.alpha_blend_source_remap = true;
|
pixel.alpha_blend_source = ShaderAlphaBlendSource::SourceAlpha;
|
||||||
const auto remapped_key = MakeStageStaticKey(pixel);
|
const auto remapped_key = MakeStageStaticKey(pixel);
|
||||||
|
pixel.alpha_blend_source = ShaderAlphaBlendSource::SourceAlphaOne;
|
||||||
|
const auto separate_key = MakeStageStaticKey(pixel);
|
||||||
Check(ordinary_key != guest_key && guest_key != remapped_key &&
|
Check(ordinary_key != guest_key && guest_key != remapped_key &&
|
||||||
ordinary_key != remapped_key,
|
ordinary_key != remapped_key && separate_key != ordinary_key &&
|
||||||
"ordinary, guest dual-source, and logical-alpha shaders share a cache key");
|
separate_key != guest_key && separate_key != remapped_key,
|
||||||
|
"ordinary, guest dual-source, and logical-alpha modes share a cache key");
|
||||||
|
|
||||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||||
options.input_info.pixel = &pixel;
|
options.input_info.pixel = &pixel;
|
||||||
for (const bool compressed : {false, true}) {
|
for (const auto mode : {ShaderAlphaBlendSource::SourceAlpha,
|
||||||
const uint32_t shader[] = {
|
ShaderAlphaBlendSource::SourceAlphaOne}) {
|
||||||
// RGBA = (1000, 2, 3, 0.25), packed into two half pairs when compressed.
|
pixel.alpha_blend_source = mode;
|
||||||
EncodeVop1(0x01, 0, 255), compressed ? 0x400063d0u : 0x447a0000u,
|
for (const bool compressed : {false, true}) {
|
||||||
EncodeVop1(0x01, 1, 255), compressed ? 0x34004200u : 0x40000000u,
|
const uint32_t shader[] = {
|
||||||
EncodeVop1(0x01, 2, 255), 0x40400000u,
|
// RGBA = (1000, 2, 3, 0.25), packed into two half pairs when compressed.
|
||||||
EncodeVop1(0x01, 3, 255), 0x3e800000u,
|
EncodeVop1(0x01, 0, 255), compressed ? 0x400063d0u : 0x447a0000u,
|
||||||
EncodeExp0(0, 0xf, false, compressed), EncodeExp1(0, 1, 2, 3),
|
EncodeVop1(0x01, 1, 255), compressed ? 0x34004200u : 0x40000000u,
|
||||||
EncodeExp0(1, 0xf, false), EncodeExp1(0, 0, 0, 0),
|
EncodeVop1(0x01, 2, 255), 0x40400000u,
|
||||||
EncodeExp0(2, 0xf, true, false, true), EncodeExp1(0, 0, 0, 0),
|
EncodeVop1(0x01, 3, 255), 0x3e800000u,
|
||||||
EncodeSopp(0x01),
|
EncodeExp0(0, 0xf, false, compressed), EncodeExp1(0, 1, 2, 3),
|
||||||
};
|
EncodeExp0(1, 0xf, false), EncodeExp1(0, 0, 0, 0),
|
||||||
const auto result = RecompileForTest(shader, options);
|
EncodeExp0(2, 0xf, true, false, true), EncodeExp1(0, 0, 0, 0),
|
||||||
CheckSpirvBinaryValidates(result.spirv);
|
EncodeSopp(0x01),
|
||||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
};
|
||||||
Check(source.find("OpDecorate %out_mrt_1 Location 0") != std::string::npos &&
|
const auto result = RecompileForTest(shader, options);
|
||||||
source.find("OpDecorate %out_mrt_1 Index 1") != std::string::npos &&
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
CountSourceOccurrences(source, "OpStore %out_mrt_1 ") == 1 &&
|
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||||
source.find("out_mrt_2") == std::string::npos,
|
Check(source.find("OpDecorate %out_mrt_1 Location 0") != std::string::npos &&
|
||||||
"inactive MRT exports overwrote the logical-alpha output");
|
source.find("OpDecorate %out_mrt_1 Index 1") != std::string::npos &&
|
||||||
Check(SpirvInstructionOpcodeCount(result.spirv, 252u) != 0,
|
CountSourceOccurrences(source, "OpStore %out_mrt_1 ") == 1 &&
|
||||||
"ignoring inactive MRT stores discarded their valid-mask export");
|
source.find("out_mrt_2") == std::string::npos,
|
||||||
uint32_t alpha_input = 0;
|
"inactive MRT exports overwrote the logical-alpha output");
|
||||||
uint32_t color_input = 0;
|
Check(SpirvInstructionOpcodeCount(result.spirv, 252u) != 0,
|
||||||
for (size_t i = 5; i < result.spirv.size(); i += result.spirv[i] >> 16u) {
|
"ignoring inactive MRT stores discarded their valid-mask export");
|
||||||
if ((result.spirv[i] & 0xffffu) != 79u || (result.spirv[i] >> 16u) != 9u) {
|
uint32_t alpha_input = 0;
|
||||||
continue;
|
uint32_t alpha_broadcast = 0;
|
||||||
}
|
uint32_t color_input = 0;
|
||||||
const auto selectors = std::span(result.spirv).subspan(i + 5, 4);
|
uint32_t one = 0;
|
||||||
if (std::ranges::equal(selectors, std::array{3u, 3u, 3u, 3u})) {
|
bool unit_alpha_factor = false;
|
||||||
alpha_input = result.spirv[i + 3];
|
for (size_t i = 5; i < result.spirv.size(); i += result.spirv[i] >> 16u) {
|
||||||
} else if (std::ranges::equal(selectors, std::array{3u, 2u, 1u, 0u})) {
|
const auto opcode = result.spirv[i] & 0xffffu;
|
||||||
color_input = result.spirv[i + 3];
|
if (opcode == 43u && (result.spirv[i] >> 16u) == 4u &&
|
||||||
|
result.spirv[i + 3] == 0x3f800000u) {
|
||||||
|
one = result.spirv[i + 2];
|
||||||
|
}
|
||||||
|
if (opcode == 82u && (result.spirv[i] >> 16u) == 6u &&
|
||||||
|
result.spirv[i + 3] == one && result.spirv[i + 4] == alpha_broadcast &&
|
||||||
|
result.spirv[i + 5] == 0u) {
|
||||||
|
unit_alpha_factor = true;
|
||||||
|
}
|
||||||
|
if ((result.spirv[i] & 0xffffu) != 79u || (result.spirv[i] >> 16u) != 9u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto selectors = std::span(result.spirv).subspan(i + 5, 4);
|
||||||
|
if (std::ranges::equal(selectors, std::array{3u, 3u, 3u, 3u})) {
|
||||||
|
alpha_input = result.spirv[i + 3];
|
||||||
|
alpha_broadcast = result.spirv[i + 2];
|
||||||
|
} else if (std::ranges::equal(selectors, std::array{3u, 2u, 1u, 0u})) {
|
||||||
|
color_input = result.spirv[i + 3];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Check(alpha_input != 0 && alpha_input == color_input,
|
||||||
|
"blend source did not broadcast logical alpha before the physical export swizzle");
|
||||||
|
Check(unit_alpha_factor == (mode == ShaderAlphaBlendSource::SourceAlphaOne),
|
||||||
|
"secondary ABGR output must be (1, Sa, Sa, Sa) only for separate alpha accumulation");
|
||||||
}
|
}
|
||||||
Check(alpha_input != 0 && alpha_input == color_input,
|
|
||||||
"blend source did not broadcast logical alpha before the physical export swizzle");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t guest_shader[] = {
|
const uint32_t guest_shader[] = {
|
||||||
EncodeExp0(0, 0xf, false), EncodeExp1(0, 1, 2, 3),
|
EncodeExp0(0, 0xf, false), EncodeExp1(0, 1, 2, 3),
|
||||||
EncodeExp0(1, 0xf), EncodeExp1(4, 5, 6, 7), EncodeSopp(0x01),
|
EncodeExp0(1, 0xf), EncodeExp1(4, 5, 6, 7), EncodeSopp(0x01),
|
||||||
};
|
};
|
||||||
pixel.alpha_blend_source_remap = false;
|
pixel.alpha_blend_source = ShaderAlphaBlendSource::None;
|
||||||
pixel.target_output_mode[1] = pixel.target_output_mode[0];
|
pixel.target_output_mode[1] = pixel.target_output_mode[0];
|
||||||
pixel.target_export_mapping = {};
|
pixel.target_export_mapping = {};
|
||||||
const auto guest = RecompileForTest(guest_shader, options);
|
const auto guest = RecompileForTest(guest_shader, options);
|
||||||
|
|||||||
Reference in New Issue
Block a user