mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 12:56:54 +01:00
shader: type pixel outputs of UINT and SINT render targets as integer vectors
Co-authored-by: Ali Almohaya <AliAlmohaya@gmail.com> Co-authored-by: Mac <me@itsmemac.com> Co-authored-by: Senaxx <michelbeld@gmail.com>
This commit is contained in:
@@ -741,6 +741,11 @@ void PipelineCache::GetGraphicsPrograms(const CommandBuffer& command, DrawRender
|
||||
const auto& rt = context.GetRenderTarget(slot);
|
||||
const auto target_mask = render_target_mask_slot(context.GetRenderTargetMask(), slot);
|
||||
if (rt.base.addr == 0 || target_mask == 0u) continue;
|
||||
if (rt.info.channel_type == Prospero::ChannelType::kUInt) {
|
||||
pixel_info.target_uint_mask |= 1u << export_index;
|
||||
} else if (rt.info.channel_type == Prospero::ChannelType::kSInt) {
|
||||
pixel_info.target_sint_mask |= 1u << export_index;
|
||||
}
|
||||
const auto format = Prospero::ResolveRenderTargetFormat(rt.info.format, rt.info.channel_type);
|
||||
if (format.buffer_format != Prospero::BufferFormat::k11_11_10UNorm) continue;
|
||||
const auto mapping = pixel_info.target_export_mapping[export_index];
|
||||
@@ -772,9 +777,11 @@ void PipelineCache::GetGraphicsPrograms(const CommandBuffer& command, DrawRender
|
||||
(blend.separate_alpha_blend && (BlendFactorIsDualSource(blend.alpha_srcblend) ||
|
||||
BlendFactorIsDualSource(blend.alpha_destblend))));
|
||||
if (pixel_info.dual_source_blending) {
|
||||
// MRT1 supplies the second blend source for target 0.
|
||||
// MRT1 supplies the second blend source for target 0; blended targets are not integer.
|
||||
pixel_info.target_output_mode[1] = pixel_info.target_output_mode[0];
|
||||
pixel_info.target_export_mapping[1] = pixel_info.target_export_mapping[0];
|
||||
pixel_info.target_uint_mask &= ~2u;
|
||||
pixel_info.target_sint_mask &= ~2u;
|
||||
} 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,
|
||||
|
||||
@@ -30,6 +30,13 @@ bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr) {
|
||||
ShaderPixelParameterIsCustom(*state.input_info.pixel, attr);
|
||||
}
|
||||
|
||||
uint32_t MrtOutputType(EmitterState& state, uint32_t index) {
|
||||
const auto& pixel = *state.input_info.pixel;
|
||||
if ((pixel.target_sint_mask & (1u << index)) != 0u) return TypeI32Vector(state, 4);
|
||||
if ((pixel.target_uint_mask & (1u << index)) != 0u) return TypeU32Vector(state, 4);
|
||||
return TypeF32Vector(state, 4);
|
||||
}
|
||||
|
||||
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp) {
|
||||
if (exp.kind == IR::ExportTargetKind::Position && exp.index == 0) {
|
||||
return state.per_vertex_variable;
|
||||
|
||||
@@ -291,9 +291,10 @@ uint32_t ExportRawComponent(ValueEmitContext& ctx, uint32_t vector, uint32_t com
|
||||
}
|
||||
|
||||
uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo& exp,
|
||||
bool uint_output) {
|
||||
auto& state = ctx.state;
|
||||
if (exp.compr && !uint_output) {
|
||||
uint32_t vector_type) {
|
||||
auto& state = ctx.state;
|
||||
const bool integer_output = vector_type != TypeF32Vector(state, 4);
|
||||
if (exp.compr && !integer_output) {
|
||||
auto unpack = GLSLstd450UnpackHalf2x16;
|
||||
switch (MrtOutputMode(state, exp)) {
|
||||
case 5: unpack = GLSLstd450UnpackUnorm2x16; break;
|
||||
@@ -330,9 +331,11 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo
|
||||
ConstantU32(state, 0),
|
||||
ConstantU32(state, 0),
|
||||
ConstantU32(state, 0),
|
||||
ConstantU32(state, uint_output ? 1u : 0x3f800000u),
|
||||
ConstantU32(state, integer_output ? 1u : 0x3f800000u),
|
||||
};
|
||||
if (exp.compr) {
|
||||
const auto extract = vector_type == TypeU32Vector(state, 4) ? spv::OpBitFieldUExtract
|
||||
: spv::OpBitFieldSExtract;
|
||||
for (uint32_t pair = 0; pair < 2u; pair++) {
|
||||
if ((exp.en & (3u << (pair * 2u))) == 0u) {
|
||||
continue;
|
||||
@@ -344,8 +347,8 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo
|
||||
continue;
|
||||
}
|
||||
raw[component] = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitFieldUExtract, TypeU32(state), raw[component],
|
||||
packed, ConstantU32(state, lane * 16u),
|
||||
state.builder.AddFunction(extract, TypeU32(state), raw[component], packed,
|
||||
ConstantU32(state, lane * 16u),
|
||||
ConstantU32(state, 16));
|
||||
}
|
||||
}
|
||||
@@ -360,12 +363,10 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo
|
||||
state.builder.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 4), data,
|
||||
raw[0], raw[1], raw[2], raw[3]);
|
||||
}
|
||||
if (uint_output) {
|
||||
if (vector_type == TypeU32Vector(state, 4)) {
|
||||
return data;
|
||||
}
|
||||
const auto vector = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeF32Vector(state, 4), vector, data);
|
||||
return vector;
|
||||
return Unary(state, spv::OpBitcast, vector_type, data);
|
||||
}
|
||||
|
||||
uint32_t DistancePointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index) {
|
||||
@@ -563,11 +564,11 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
if (state.program.stage != ShaderType::Mesh && variable == 0) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
const auto vector_type = MrtOutputMode(state, exp) != 0u ? MrtOutputType(state, exp.index)
|
||||
: TypeF32Vector(state, 4);
|
||||
auto value = ExportVector(ctx, data, exp, vector_type);
|
||||
if (state.program.stage == ShaderType::Pixel && exp.kind == IR::ExportTargetKind::Mrt &&
|
||||
exp.index == 0 && !uint_output &&
|
||||
exp.index == 0 && vector_type == TypeF32Vector(state, 4) &&
|
||||
state.input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None) {
|
||||
// Broadcast logical alpha before swizzling the primary output.
|
||||
const auto blend_output =
|
||||
|
||||
@@ -307,6 +307,7 @@ uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind ki
|
||||
|
||||
|
||||
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp);
|
||||
uint32_t MrtOutputType(EmitterState& state, uint32_t index);
|
||||
|
||||
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
||||
|
||||
|
||||
@@ -691,13 +691,15 @@ void DefineOutputs(EmitterState& state) {
|
||||
break;
|
||||
case IR::StageOutputKind::Parameter:
|
||||
case IR::StageOutputKind::Mrt: {
|
||||
const bool uint_output =
|
||||
binding.kind == IR::StageOutputKind::Mrt &&
|
||||
state.program.stage == ShaderType::Pixel &&
|
||||
binding.index < std::size(state.input_info.pixel->target_output_mode) &&
|
||||
(state.input_info.pixel->target_output_mode[binding.index] == 7u ||
|
||||
(state.input_info.pixel->target_packed_unorm_mask & (1u << binding.index)) != 0u);
|
||||
const auto type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
||||
auto type = TypeF32Vector(state, 4);
|
||||
if (binding.kind == IR::StageOutputKind::Mrt &&
|
||||
state.program.stage == ShaderType::Pixel && binding.index < 8u) {
|
||||
// Packed UNorm stores its encoded bits.
|
||||
const auto bit = 1u << binding.index;
|
||||
type = (state.input_info.pixel->target_packed_unorm_mask & bit) != 0u
|
||||
? TypeU32Vector(state, 4)
|
||||
: MrtOutputType(state, binding.index);
|
||||
}
|
||||
binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput,
|
||||
binding.debug_name.c_str());
|
||||
const bool dual_source = binding.kind == IR::StageOutputKind::Mrt &&
|
||||
|
||||
@@ -490,7 +490,9 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
||||
key.push_back(static_cast<uint32_t>(info.object_primitive_id));
|
||||
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.push_back(info.target_packed_unorm_mask | (uint32_t {info.target_round_mode} << 8u));
|
||||
key.push_back(info.target_packed_unorm_mask | (uint32_t {info.target_round_mode} << 8u) |
|
||||
(uint32_t {info.target_uint_mask} << 16u) |
|
||||
(uint32_t {info.target_sint_mask} << 24u));
|
||||
const auto mask = info.target_shader_mask;
|
||||
key.push_back((mask | (mask >> 1u) | (mask >> 2u) | (mask >> 3u)) & 0x11111111u);
|
||||
for (uint32_t base = 0; base < info.target_export_mapping.size(); base += 4u) {
|
||||
|
||||
@@ -181,6 +181,9 @@ struct ShaderPixelInputInfo {
|
||||
uint32_t target_shader_mask = UINT32_MAX;
|
||||
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
||||
uint8_t target_packed_unorm_mask = 0;
|
||||
// Exports to UINT and SINT targets, which take integer outputs of the same signedness.
|
||||
uint8_t target_uint_mask = 0;
|
||||
uint8_t target_sint_mask = 0;
|
||||
// CB round mode per converted export: zero adds half an LSB, one truncates.
|
||||
uint8_t target_round_mode = 0;
|
||||
uint32_t scratch_size_dwords = 0;
|
||||
|
||||
@@ -19808,6 +19808,40 @@ public:
|
||||
}
|
||||
Require(name, "packed rounding program distinction", half_up_program != truncate_program,
|
||||
"half-up and truncate exports shared one shader specialization");
|
||||
// UINT and SINT targets give their exports integer outputs.
|
||||
registers.Reset();
|
||||
registers.SetViewportTransformControl(0x300);
|
||||
registers.SetRenderTargetMask(0xffu);
|
||||
registers.SetShaderMask(0xffu);
|
||||
shaders.SetPsShaderBase(pixel_address);
|
||||
for (const auto [slot, type] : {std::pair{0u, Prospero::ChannelType::kUInt},
|
||||
std::pair{1u, Prospero::ChannelType::kSInt}}) {
|
||||
registers.SetTargetOutputMode(slot, 9);
|
||||
registers.SetColorBase(slot, {.addr = depth_address + 0x10000 * (slot + 1)});
|
||||
registers.SetColorInfo(slot, {.format = Prospero::ChannelLayout::k32, .channel_type = type,
|
||||
.channel_order = Prospero::ChannelOrder::kStandard});
|
||||
registers.SetColorAttrib2(slot, {.height = extent - 1, .width = extent - 1});
|
||||
registers.SetColorAttrib3(slot, {.tile_mode = Prospero::TileMode::kLinear, .dimension = 1});
|
||||
}
|
||||
native_programs();
|
||||
Require(name, "integer target outputs",
|
||||
pixel.target_uint_mask == 1u && pixel.target_sint_mask == 2u,
|
||||
"UINT and SINT targets did not select integer export outputs");
|
||||
// MRT1 is the second blend source of a dual-source float target 0, not integer target 1.
|
||||
HW::BlendControl dual_blend{};
|
||||
dual_blend.enable = true;
|
||||
dual_blend.color_srcblend = static_cast<uint8_t>(Prospero::BlendFactor::kSrc1Color);
|
||||
registers.SetBlendControl(0, dual_blend);
|
||||
for (const auto [slot, type] : {std::pair{0u, Prospero::ChannelType::kFloat},
|
||||
std::pair{1u, Prospero::ChannelType::kUInt}}) {
|
||||
registers.SetColorInfo(slot, {.format = Prospero::ChannelLayout::k32, .channel_type = type,
|
||||
.channel_order = Prospero::ChannelOrder::kStandard});
|
||||
native_programs();
|
||||
Require(name, "dual-source float outputs",
|
||||
pixel.dual_source_blending && pixel.target_uint_mask == 0u &&
|
||||
pixel.target_sint_mask == 0u,
|
||||
"the second blend source kept the integer type of render target 1");
|
||||
}
|
||||
DestroyBuffer(&packed_output);
|
||||
m_device.destroySampler(packed_sampler);
|
||||
DestroyBuffer(&stencil_readback);
|
||||
|
||||
@@ -13748,6 +13748,7 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
||||
|
||||
ShaderPixelInputInfo uint16_info;
|
||||
uint16_info.target_output_mode[0] = 7;
|
||||
uint16_info.target_uint_mask = 1;
|
||||
options.input_info.pixel = &uint16_info;
|
||||
auto uint16_result = RecompileForTest(shader, options);
|
||||
const auto uint16_source = DisassembleSpirvBinary(uint16_result.spirv);
|
||||
@@ -13762,6 +13763,18 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
||||
"compressed UINT16 MRT export was incorrectly decoded as FP16");
|
||||
CheckSpirvBinaryValidates(uint16_result.spirv);
|
||||
|
||||
ShaderPixelInputInfo sint16_info;
|
||||
sint16_info.target_output_mode[0] = 8;
|
||||
sint16_info.target_sint_mask = 1;
|
||||
options.input_info.pixel = &sint16_info;
|
||||
auto sint16_result = RecompileForTest(shader, options);
|
||||
const auto sint16_source = DisassembleSpirvBinary(sint16_result.spirv);
|
||||
Check(sint16_source.find("OpVariable %_ptr_Output_v4int Output") != std::string::npos &&
|
||||
CountSourceOccurrences(sint16_source, "OpBitFieldSExtract") == 4u &&
|
||||
!SpirvContainsExtInst(sint16_result.spirv, 62),
|
||||
"compressed SINT16 MRT export did not sign-extend into a signed output");
|
||||
CheckSpirvBinaryValidates(sint16_result.spirv);
|
||||
|
||||
ShaderPixelInputInfo unorm16_info;
|
||||
unorm16_info.target_output_mode[0] = 5;
|
||||
options.input_info.pixel = &unorm16_info;
|
||||
@@ -13789,6 +13802,7 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
||||
|
||||
ShaderPixelInputInfo uint_info;
|
||||
uint_info.target_output_mode[0] = 7;
|
||||
uint_info.target_uint_mask = 1;
|
||||
options.input_info.pixel = &uint_info;
|
||||
auto partial_uint_result =
|
||||
RecompileForTest(partial_shader, options);
|
||||
@@ -13815,6 +13829,15 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
||||
"compressed UINT16 BA-only export did not read and unpack VSRC1");
|
||||
CheckSpirvBinaryValidates(compressed_ba_result.spirv);
|
||||
|
||||
// 32-bit exports keep their raw bits in a UINT target's integer output.
|
||||
uint_info.target_output_mode[0] = 9;
|
||||
auto raw_uint_result = RecompileForTest(partial_shader, options);
|
||||
const auto raw_uint_source = DisassembleSpirvBinary(raw_uint_result.spirv);
|
||||
Check(raw_uint_source.find("OpVariable %_ptr_Output_v4uint Output") != std::string::npos &&
|
||||
raw_uint_source.find("OpBitcast %v4float") == std::string::npos,
|
||||
"32-bit MRT export to a UINT target did not use an unsigned integer output");
|
||||
CheckSpirvBinaryValidates(raw_uint_result.spirv);
|
||||
|
||||
unorm16_info.target_output_mode[0] = 5;
|
||||
options.input_info.pixel = &unorm16_info;
|
||||
auto unorm16_ba_result =
|
||||
|
||||
Reference in New Issue
Block a user