mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
graphics: preserve logical alpha when blending reordered targets (PPSA02721)
Co-authored-by: EmK530 <admin@emk530.net>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
#include "graphics/host_gpu/renderer/pipeline/blendMapping.h"
|
||||
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
bool BlendFactorIsDualSource(uint8_t factor) {
|
||||
return factor >= static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Color) &&
|
||||
factor <= static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrc1Alpha);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool BlendFactorIsConstantColor(uint8_t factor) {
|
||||
return factor == static_cast<uint8_t>(Prospero::BlendFactor::kConstantColor) ||
|
||||
factor == static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusConstantColor);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BlendMappingSupport ClassifyBlendMapping(const HW::BlendControl& blend,
|
||||
const Prospero::ColorComponentMapping& mapping) {
|
||||
// Color constants are not swizzled with the exports; scalar constant alpha is unaffected.
|
||||
if (!mapping.IsIdentity() && (BlendFactorIsConstantColor(blend.color_srcblend) ||
|
||||
BlendFactorIsConstantColor(blend.color_destblend))) {
|
||||
return BlendMappingSupport::Unsupported;
|
||||
}
|
||||
if (mapping.Map(3) == 3u) {
|
||||
return BlendMappingSupport::Direct;
|
||||
}
|
||||
// Moving alpha requires the same equation for all channels.
|
||||
if (blend.separate_alpha_blend && (blend.alpha_srcblend != blend.color_srcblend ||
|
||||
blend.alpha_destblend != blend.color_destblend ||
|
||||
blend.alpha_comb_fcn != blend.color_comb_fcn)) {
|
||||
return BlendMappingSupport::Unsupported;
|
||||
}
|
||||
auto support = BlendMappingSupport::Direct;
|
||||
for (const auto factor: {blend.color_srcblend, blend.color_destblend}) {
|
||||
if (BlendFactorIsDualSource(factor)) {
|
||||
return BlendMappingSupport::Unsupported;
|
||||
}
|
||||
switch (static_cast<Prospero::BlendFactor>(factor)) {
|
||||
case Prospero::BlendFactor::kSrcAlpha:
|
||||
case Prospero::BlendFactor::kOneMinusSrcAlpha:
|
||||
support = BlendMappingSupport::SourceAlpha;
|
||||
break;
|
||||
case Prospero::BlendFactor::kDstAlpha:
|
||||
case Prospero::BlendFactor::kOneMinusDstAlpha:
|
||||
case Prospero::BlendFactor::kSrcAlphaSaturate: return BlendMappingSupport::Unsupported;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_PIPELINE_BLENDMAPPING_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_PIPELINE_BLENDMAPPING_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace HW {
|
||||
struct BlendControl;
|
||||
}
|
||||
namespace Prospero {
|
||||
struct ColorComponentMapping;
|
||||
}
|
||||
|
||||
enum class BlendMappingSupport {
|
||||
Direct,
|
||||
SourceAlpha, // Requires logical alpha in the second blend source.
|
||||
Unsupported,
|
||||
};
|
||||
|
||||
bool BlendFactorIsDualSource(uint8_t factor);
|
||||
BlendMappingSupport ClassifyBlendMapping(const HW::BlendControl& blend,
|
||||
const Prospero::ColorComponentMapping& mapping);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_PIPELINE_BLENDMAPPING_H_
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/blendMapping.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
||||
@@ -609,21 +610,30 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
|
||||
}
|
||||
ShaderParams pixel_params;
|
||||
if (pixel_active) {
|
||||
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
|
||||
const auto& blend = context.GetBlendControl(0);
|
||||
const auto is_dual_source = [](uint8_t factor) {
|
||||
return factor >= static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Color) &&
|
||||
factor <= static_cast<uint8_t>(Prospero::BlendFactor::kOneMinusSrc1Alpha);
|
||||
};
|
||||
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
|
||||
const auto& blend = context.GetBlendControl(0);
|
||||
pixel_info.dual_source_blending =
|
||||
blend.enable && !context.GetRenderTarget(0).info.blend_bypass &&
|
||||
(is_dual_source(blend.color_srcblend) || is_dual_source(blend.color_destblend) ||
|
||||
(blend.separate_alpha_blend &&
|
||||
(is_dual_source(blend.alpha_srcblend) || is_dual_source(blend.alpha_destblend))));
|
||||
(BlendFactorIsDualSource(blend.color_srcblend) ||
|
||||
BlendFactorIsDualSource(blend.color_destblend) ||
|
||||
(blend.separate_alpha_blend && (BlendFactorIsDualSource(blend.alpha_srcblend) ||
|
||||
BlendFactorIsDualSource(blend.alpha_destblend))));
|
||||
if (pixel_info.dual_source_blending) {
|
||||
// MRT1 supplies a second blend source for the same render target as MRT0.
|
||||
// MRT1 supplies the second blend source for target 0.
|
||||
pixel_info.target_output_mode[1] = pixel_info.target_output_mode[0];
|
||||
pixel_info.target_export_mapping[1] = pixel_info.target_export_mapping[0];
|
||||
} else if (blend.enable && !context.GetRenderTarget(0).info.blend_bypass &&
|
||||
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::end(pixel_info.target_output_mode),
|
||||
[](uint8_t mode) { return mode == 0; }) &&
|
||||
ClassifyBlendMapping(blend, pixel_info.target_export_mapping[0]) ==
|
||||
BlendMappingSupport::SourceAlpha) {
|
||||
// Preserve logical alpha when the export mapping moves it.
|
||||
pixel_info.alpha_blend_source_remap = true;
|
||||
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) {
|
||||
@@ -720,7 +730,24 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
||||
static_params.alpha_comb_fcn[slot] = bc.alpha_comb_fcn;
|
||||
static_params.alpha_destblend[slot] = bc.alpha_destblend;
|
||||
static_params.separate_alpha_blend[slot] = bc.separate_alpha_blend;
|
||||
static_params.blend_enable[slot] = bc.enable && !rt.info.blend_bypass;
|
||||
const bool alpha_remap =
|
||||
slot == 0 && ps_input_info != nullptr && ps_input_info->alpha_blend_source_remap;
|
||||
static_params.blend_enable[slot] = bc.enable && !rt.info.blend_bypass;
|
||||
if (static_params.blend_enable[slot] && !alpha_remap &&
|
||||
ClassifyBlendMapping(bc, colors[i].export_mapping) != BlendMappingSupport::Direct) {
|
||||
static_params.blend_enable[slot] = false;
|
||||
static std::atomic_bool warned = false;
|
||||
if (!warned.exchange(true, std::memory_order_relaxed)) {
|
||||
Log::WriteToConsoleAndLog(fmt::format(
|
||||
"Warning: blending disabled for unsupported color mapping "
|
||||
"(slot={} mapping=0x{:02x} color={}/{} alpha={}/{} separate={}).\n",
|
||||
slot, colors[i].export_mapping.packed, bc.color_srcblend, bc.color_destblend,
|
||||
bc.alpha_srcblend, bc.alpha_destblend, bc.separate_alpha_blend ? 1 : 0));
|
||||
}
|
||||
}
|
||||
if (alpha_remap) {
|
||||
static_params.blend_alpha_source_remap = true;
|
||||
}
|
||||
}
|
||||
const bool with_depth =
|
||||
depth.desc.view_info.format != vk::Format::eUndefined && static_cast<bool>(depth.image_id);
|
||||
|
||||
@@ -55,6 +55,7 @@ struct PipelineStaticParameters {
|
||||
uint8_t alpha_destblend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
bool separate_alpha_blend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
bool blend_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
bool blend_alpha_source_remap = false;
|
||||
|
||||
bool operator==(const PipelineStaticParameters& other) const noexcept;
|
||||
};
|
||||
@@ -64,7 +65,7 @@ struct PipelineStaticParameters {
|
||||
static_assert(std::is_trivially_copyable_v<PipelineStaticParameters>);
|
||||
static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
||||
static_assert(alignof(PipelineStaticParameters) == 1);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 125);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 126);
|
||||
|
||||
struct PipelineRenderingState {
|
||||
std::array<vk::Format, RENDER_COLOR_ATTACHMENTS_MAX> color_formats {};
|
||||
|
||||
@@ -137,14 +137,17 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
|
||||
}
|
||||
}
|
||||
|
||||
static vk::BlendFactor GetBlendFactor(uint32_t factor) {
|
||||
static vk::BlendFactor GetBlendFactor(uint32_t factor, bool remap_source_alpha) {
|
||||
switch (static_cast<Prospero::BlendFactor>(factor)) {
|
||||
case Prospero::BlendFactor::kZero: return vk::BlendFactor::eZero;
|
||||
case Prospero::BlendFactor::kOne: return vk::BlendFactor::eOne;
|
||||
case Prospero::BlendFactor::kSrcColor: return vk::BlendFactor::eSrcColor;
|
||||
case Prospero::BlendFactor::kOneMinusSrcColor: return vk::BlendFactor::eOneMinusSrcColor;
|
||||
case Prospero::BlendFactor::kSrcAlpha: return vk::BlendFactor::eSrcAlpha;
|
||||
case Prospero::BlendFactor::kOneMinusSrcAlpha: return vk::BlendFactor::eOneMinusSrcAlpha;
|
||||
case Prospero::BlendFactor::kSrcAlpha:
|
||||
return remap_source_alpha ? vk::BlendFactor::eSrc1Color : vk::BlendFactor::eSrcAlpha;
|
||||
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::kOneMinusDstAlpha: return vk::BlendFactor::eOneMinusDstAlpha;
|
||||
case Prospero::BlendFactor::kDstColor: return vk::BlendFactor::eDstColor;
|
||||
@@ -397,17 +400,20 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& p
|
||||
color_blend_attachment[i].colorWriteMask =
|
||||
vk::ColorComponentFlags {static_params.color_mask[i]};
|
||||
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 =
|
||||
GetBlendFactor(static_params.color_srcblend[i]);
|
||||
GetBlendFactor(static_params.color_srcblend[i], remap_source_alpha);
|
||||
color_blend_attachment[i].dstColorBlendFactor =
|
||||
GetBlendFactor(static_params.color_destblend[i]);
|
||||
GetBlendFactor(static_params.color_destblend[i], remap_source_alpha);
|
||||
color_blend_attachment[i].colorBlendOp = GetBlendOp(static_params.color_comb_fcn[i]);
|
||||
color_blend_attachment[i].srcAlphaBlendFactor =
|
||||
(static_params.separate_alpha_blend[i] ? GetBlendFactor(static_params.alpha_srcblend[i])
|
||||
: color_blend_attachment[i].srcColorBlendFactor);
|
||||
(static_params.separate_alpha_blend[i]
|
||||
? GetBlendFactor(static_params.alpha_srcblend[i], remap_source_alpha)
|
||||
: color_blend_attachment[i].srcColorBlendFactor);
|
||||
color_blend_attachment[i].dstAlphaBlendFactor =
|
||||
(static_params.separate_alpha_blend[i]
|
||||
? GetBlendFactor(static_params.alpha_destblend[i])
|
||||
? GetBlendFactor(static_params.alpha_destblend[i], remap_source_alpha)
|
||||
: color_blend_attachment[i].dstColorBlendFactor);
|
||||
color_blend_attachment[i].alphaBlendOp =
|
||||
(static_params.separate_alpha_blend[i] ? GetBlendOp(static_params.alpha_comb_fcn[i])
|
||||
|
||||
@@ -452,6 +452,11 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
if (exp.kind == IR::ExportTargetKind::Null || exp.en == 0u) {
|
||||
return;
|
||||
}
|
||||
// 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 &&
|
||||
exp.index != 0 && state.input_info.pixel->alpha_blend_source_remap) {
|
||||
return;
|
||||
}
|
||||
EmitIfCondition(state, exec, [&]() {
|
||||
const auto data = ctx.Arg(inst, 0);
|
||||
if (exp.kind == IR::ExportTargetKind::Primitive) {
|
||||
@@ -492,6 +497,18 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const bool uint_output = MrtOutputMode(state, exp) == 7u;
|
||||
const auto vector_type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
||||
auto value = ExportVector(ctx, data, exp, uint_output);
|
||||
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
||||
exp.index == 0 && !uint_output && state.input_info.pixel->alpha_blend_source_remap) {
|
||||
// Broadcast logical alpha before swizzling the primary output.
|
||||
const auto blend_output =
|
||||
OutputVariableForExport(state, {.kind = IR::ExportTargetKind::Mrt, .index = 1});
|
||||
if (blend_output != 0) {
|
||||
const auto alpha = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpVectorShuffle, vector_type, alpha, value, value,
|
||||
3u, 3u, 3u, 3u);
|
||||
state.builder.AddFunction(spv::OpStore, blend_output, alpha);
|
||||
}
|
||||
}
|
||||
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
||||
exp.index < state.input_info.pixel->target_export_mapping.size()) {
|
||||
const auto mapping = state.input_info.pixel->target_export_mapping[exp.index];
|
||||
|
||||
@@ -326,6 +326,8 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
|
||||
}
|
||||
|
||||
void CollectOutputs(const Program& program, ShaderStageInputInfo input_info, ShaderInfo& info) {
|
||||
const bool alpha_remap = program.stage == ShaderType::Pixel && input_info.pixel != nullptr &&
|
||||
input_info.pixel->alpha_blend_source_remap;
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::SetAttribute) {
|
||||
@@ -385,8 +387,14 @@ void CollectOutputs(const Program& program, ShaderStageInputInfo input_info, Sha
|
||||
export_info.index, fmt::format("out_param_{}", export_info.index));
|
||||
break;
|
||||
case ExportTargetKind::Mrt:
|
||||
if (alpha_remap && export_info.index != 0) {
|
||||
break;
|
||||
}
|
||||
AddOutput(info, StageOutputKind::Mrt, export_info.index, export_info.index,
|
||||
fmt::format("out_mrt_{}", export_info.index));
|
||||
if (alpha_remap) {
|
||||
AddOutput(info, StageOutputKind::Mrt, 1, 1, "out_mrt_1");
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -693,6 +693,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_early_z));
|
||||
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
|
||||
key.push_back(static_cast<uint32_t>(info.alpha_blend_source_remap));
|
||||
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) {
|
||||
uint32_t packed = 0;
|
||||
|
||||
@@ -178,6 +178,8 @@ struct ShaderPixelInputInfo {
|
||||
bool ps_sample_mask_export_enable = false;
|
||||
bool ps_sample_shading = false;
|
||||
bool dual_source_blending = false;
|
||||
// Export logical alpha through MRT1 for blending after channel swizzling.
|
||||
bool alpha_blend_source_remap = false;
|
||||
bool ps_early_z = false;
|
||||
bool ps_execute_on_noop = false;
|
||||
ShaderStageRuntime stage;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "graphics/guest_gpu/pm4.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/blendMapping.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
|
||||
@@ -12281,6 +12282,116 @@ void TestRenderTargetReverseExportMapping() {
|
||||
"active reverse MRT mapping was lost before shader specialization");
|
||||
}
|
||||
|
||||
void TestBlendMappingClassification() {
|
||||
using Factor = Prospero::BlendFactor;
|
||||
using Support = BlendMappingSupport;
|
||||
HW::BlendControl blend{};
|
||||
blend.separate_alpha_blend = false;
|
||||
const auto classify = [&](Prospero::ColorComponentMapping mapping) {
|
||||
return ClassifyBlendMapping(blend, mapping);
|
||||
};
|
||||
for (const auto factor : {Factor::kConstantAlpha, Factor::kOneMinusConstantAlpha}) {
|
||||
blend.color_srcblend = static_cast<uint8_t>(factor);
|
||||
Check(classify(Prospero::ColorMappingAbgr) == Support::Direct,
|
||||
"scalar blend constant incorrectly required physical alpha");
|
||||
}
|
||||
for (const auto factor : {Factor::kConstantColor, Factor::kOneMinusConstantColor}) {
|
||||
blend.color_srcblend = static_cast<uint8_t>(factor);
|
||||
Check(classify(Prospero::ColorMappingRgba) == Support::Direct &&
|
||||
classify(Prospero::ColorMappingBgra) == Support::Unsupported &&
|
||||
classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
||||
"logical blend constants were accepted with shuffled color components");
|
||||
}
|
||||
blend.color_srcblend = static_cast<uint8_t>(Factor::kOne);
|
||||
blend.alpha_srcblend = static_cast<uint8_t>(Factor::kConstantColor);
|
||||
blend.separate_alpha_blend = true;
|
||||
Check(classify(Prospero::ColorMappingBgra) == Support::Direct &&
|
||||
classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
||||
"separate constant-color alpha equation ignored the physical alpha location");
|
||||
blend.color_srcblend = static_cast<uint8_t>(Factor::kSrcAlpha);
|
||||
blend.separate_alpha_blend = false;
|
||||
Check(classify(Prospero::ColorMappingAbgr) == Support::SourceAlpha,
|
||||
"reversed source-alpha blending did not request logical alpha");
|
||||
blend.separate_alpha_blend = true;
|
||||
Check(classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
||||
"different alpha equations were accepted on a reversed target");
|
||||
blend.separate_alpha_blend = false;
|
||||
blend.color_srcblend = static_cast<uint8_t>(Factor::kDstAlpha);
|
||||
Check(classify(Prospero::ColorMappingAbgr) == Support::Unsupported,
|
||||
"destination alpha incorrectly used the physical alpha channel");
|
||||
}
|
||||
|
||||
void TestLogicalAlphaBlendExport() {
|
||||
ShaderPixelInputInfo pixel{};
|
||||
pixel.target_output_mode[0] = 4;
|
||||
pixel.target_export_mapping[0] = Prospero::ColorMappingAbgr;
|
||||
const auto ordinary_key = MakeStageStaticKey(pixel);
|
||||
pixel.dual_source_blending = true;
|
||||
const auto guest_key = MakeStageStaticKey(pixel);
|
||||
pixel.alpha_blend_source_remap = true;
|
||||
const auto remapped_key = MakeStageStaticKey(pixel);
|
||||
Check(ordinary_key != guest_key && guest_key != remapped_key &&
|
||||
ordinary_key != remapped_key,
|
||||
"ordinary, guest dual-source, and logical-alpha shaders share a cache key");
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.input_info.pixel = &pixel;
|
||||
for (const bool compressed : {false, true}) {
|
||||
const uint32_t shader[] = {
|
||||
// RGBA = (1000, 2, 3, 0.25), packed into two half pairs when compressed.
|
||||
EncodeVop1(0x01, 0, 255), compressed ? 0x400063d0u : 0x447a0000u,
|
||||
EncodeVop1(0x01, 1, 255), compressed ? 0x34004200u : 0x40000000u,
|
||||
EncodeVop1(0x01, 2, 255), 0x40400000u,
|
||||
EncodeVop1(0x01, 3, 255), 0x3e800000u,
|
||||
EncodeExp0(0, 0xf, false, compressed), EncodeExp1(0, 1, 2, 3),
|
||||
EncodeExp0(1, 0xf, false), EncodeExp1(0, 0, 0, 0),
|
||||
EncodeExp0(2, 0xf, true, false, true), EncodeExp1(0, 0, 0, 0),
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
const auto result = RecompileForTest(shader, options);
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(source.find("OpDecorate %out_mrt_1 Location 0") != std::string::npos &&
|
||||
source.find("OpDecorate %out_mrt_1 Index 1") != std::string::npos &&
|
||||
CountSourceOccurrences(source, "OpStore %out_mrt_1 ") == 1 &&
|
||||
source.find("out_mrt_2") == std::string::npos,
|
||||
"inactive MRT exports overwrote the logical-alpha output");
|
||||
Check(SpirvInstructionOpcodeCount(result.spirv, 252u) != 0,
|
||||
"ignoring inactive MRT stores discarded their valid-mask export");
|
||||
uint32_t alpha_input = 0;
|
||||
uint32_t color_input = 0;
|
||||
for (size_t i = 5; i < result.spirv.size(); i += result.spirv[i] >> 16u) {
|
||||
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];
|
||||
} 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");
|
||||
}
|
||||
|
||||
const uint32_t guest_shader[] = {
|
||||
EncodeExp0(0, 0xf, false), EncodeExp1(0, 1, 2, 3),
|
||||
EncodeExp0(1, 0xf), EncodeExp1(4, 5, 6, 7), EncodeSopp(0x01),
|
||||
};
|
||||
pixel.alpha_blend_source_remap = false;
|
||||
pixel.target_output_mode[1] = pixel.target_output_mode[0];
|
||||
pixel.target_export_mapping = {};
|
||||
const auto guest = RecompileForTest(guest_shader, options);
|
||||
CheckSpirvBinaryValidates(guest.spirv);
|
||||
const auto source = DisassembleSpirvBinary(guest.spirv);
|
||||
Check(source.find("OpDecorate %out_mrt_1 Location 0") != std::string::npos &&
|
||||
source.find("OpDecorate %out_mrt_1 Index 1") != std::string::npos &&
|
||||
CountSourceOccurrences(source, "OpStore %out_mrt_1 ") == 1 &&
|
||||
SpirvInstructionOpcodeCount(guest.spirv, 81u) == 8u,
|
||||
"guest dual-source export was replaced by synthetic alpha");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled() {
|
||||
constexpr uint32_t ExecutionModeEarlyFragmentTests = 9;
|
||||
|
||||
@@ -14043,6 +14154,8 @@ int main() {
|
||||
TestNewShaderRecompilerPerInvocationU64Complement();
|
||||
TestNewShaderRecompilerExpPixelOutputs();
|
||||
TestRenderTargetReverseExportMapping();
|
||||
TestBlendMappingClassification();
|
||||
TestLogicalAlphaBlendExport();
|
||||
TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled();
|
||||
TestTypedDescriptorRealWideMoveTranslation();
|
||||
TestComputeImageFill();
|
||||
|
||||
Reference in New Issue
Block a user