mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +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& rt = context.GetRenderTarget(slot);
|
||||||
const auto target_mask = render_target_mask_slot(context.GetRenderTargetMask(), slot);
|
const auto target_mask = render_target_mask_slot(context.GetRenderTargetMask(), slot);
|
||||||
if (rt.base.addr == 0 || target_mask == 0u) continue;
|
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);
|
const auto format = Prospero::ResolveRenderTargetFormat(rt.info.format, rt.info.channel_type);
|
||||||
if (format.buffer_format != Prospero::BufferFormat::k11_11_10UNorm) continue;
|
if (format.buffer_format != Prospero::BufferFormat::k11_11_10UNorm) continue;
|
||||||
const auto mapping = pixel_info.target_export_mapping[export_index];
|
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) ||
|
(blend.separate_alpha_blend && (BlendFactorIsDualSource(blend.alpha_srcblend) ||
|
||||||
BlendFactorIsDualSource(blend.alpha_destblend))));
|
BlendFactorIsDualSource(blend.alpha_destblend))));
|
||||||
if (pixel_info.dual_source_blending) {
|
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_output_mode[1] = pixel_info.target_output_mode[0];
|
||||||
pixel_info.target_export_mapping[1] = pixel_info.target_export_mapping[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 &&
|
} else if (blend.enable && !context.GetRenderTarget(0).info.blend_bypass &&
|
||||||
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,
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr) {
|
|||||||
ShaderPixelParameterIsCustom(*state.input_info.pixel, 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) {
|
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp) {
|
||||||
if (exp.kind == IR::ExportTargetKind::Position && exp.index == 0) {
|
if (exp.kind == IR::ExportTargetKind::Position && exp.index == 0) {
|
||||||
return state.per_vertex_variable;
|
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,
|
uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo& exp,
|
||||||
bool uint_output) {
|
uint32_t vector_type) {
|
||||||
auto& state = ctx.state;
|
auto& state = ctx.state;
|
||||||
if (exp.compr && !uint_output) {
|
const bool integer_output = vector_type != TypeF32Vector(state, 4);
|
||||||
|
if (exp.compr && !integer_output) {
|
||||||
auto unpack = GLSLstd450UnpackHalf2x16;
|
auto unpack = GLSLstd450UnpackHalf2x16;
|
||||||
switch (MrtOutputMode(state, exp)) {
|
switch (MrtOutputMode(state, exp)) {
|
||||||
case 5: unpack = GLSLstd450UnpackUnorm2x16; break;
|
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, 0),
|
||||||
ConstantU32(state, 0),
|
ConstantU32(state, 0),
|
||||||
ConstantU32(state, uint_output ? 1u : 0x3f800000u),
|
ConstantU32(state, integer_output ? 1u : 0x3f800000u),
|
||||||
};
|
};
|
||||||
if (exp.compr) {
|
if (exp.compr) {
|
||||||
|
const auto extract = vector_type == TypeU32Vector(state, 4) ? spv::OpBitFieldUExtract
|
||||||
|
: spv::OpBitFieldSExtract;
|
||||||
for (uint32_t pair = 0; pair < 2u; pair++) {
|
for (uint32_t pair = 0; pair < 2u; pair++) {
|
||||||
if ((exp.en & (3u << (pair * 2u))) == 0u) {
|
if ((exp.en & (3u << (pair * 2u))) == 0u) {
|
||||||
continue;
|
continue;
|
||||||
@@ -344,8 +347,8 @@ uint32_t ExportVector(ValueEmitContext& ctx, uint32_t data, const IR::ExportInfo
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
raw[component] = state.builder.AllocateId();
|
raw[component] = state.builder.AllocateId();
|
||||||
state.builder.AddFunction(spv::OpBitFieldUExtract, TypeU32(state), raw[component],
|
state.builder.AddFunction(extract, TypeU32(state), raw[component], packed,
|
||||||
packed, ConstantU32(state, lane * 16u),
|
ConstantU32(state, lane * 16u),
|
||||||
ConstantU32(state, 16));
|
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,
|
state.builder.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 4), data,
|
||||||
raw[0], raw[1], raw[2], raw[3]);
|
raw[0], raw[1], raw[2], raw[3]);
|
||||||
}
|
}
|
||||||
if (uint_output) {
|
if (vector_type == TypeU32Vector(state, 4)) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
const auto vector = state.builder.AllocateId();
|
return Unary(state, spv::OpBitcast, vector_type, data);
|
||||||
state.builder.AddFunction(spv::OpBitcast, TypeF32Vector(state, 4), vector, data);
|
|
||||||
return vector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t DistancePointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index) {
|
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) {
|
if (state.program.stage != ShaderType::Mesh && variable == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const bool uint_output = MrtOutputMode(state, exp) == 7u;
|
const auto vector_type = MrtOutputMode(state, exp) != 0u ? MrtOutputType(state, exp.index)
|
||||||
const auto vector_type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
: TypeF32Vector(state, 4);
|
||||||
auto value = ExportVector(ctx, data, exp, uint_output);
|
auto value = ExportVector(ctx, data, exp, vector_type);
|
||||||
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 &&
|
exp.index == 0 && vector_type == TypeF32Vector(state, 4) &&
|
||||||
state.input_info.pixel->alpha_blend_source != ShaderAlphaBlendSource::None) {
|
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 =
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind ki
|
|||||||
|
|
||||||
|
|
||||||
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp);
|
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);
|
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
||||||
|
|
||||||
|
|||||||
@@ -691,13 +691,15 @@ void DefineOutputs(EmitterState& state) {
|
|||||||
break;
|
break;
|
||||||
case IR::StageOutputKind::Parameter:
|
case IR::StageOutputKind::Parameter:
|
||||||
case IR::StageOutputKind::Mrt: {
|
case IR::StageOutputKind::Mrt: {
|
||||||
const bool uint_output =
|
auto type = TypeF32Vector(state, 4);
|
||||||
binding.kind == IR::StageOutputKind::Mrt &&
|
if (binding.kind == IR::StageOutputKind::Mrt &&
|
||||||
state.program.stage == ShaderType::Pixel &&
|
state.program.stage == ShaderType::Pixel && binding.index < 8u) {
|
||||||
binding.index < std::size(state.input_info.pixel->target_output_mode) &&
|
// Packed UNorm stores its encoded bits.
|
||||||
(state.input_info.pixel->target_output_mode[binding.index] == 7u ||
|
const auto bit = 1u << binding.index;
|
||||||
(state.input_info.pixel->target_packed_unorm_mask & (1u << binding.index)) != 0u);
|
type = (state.input_info.pixel->target_packed_unorm_mask & bit) != 0u
|
||||||
const auto type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
? TypeU32Vector(state, 4)
|
||||||
|
: MrtOutputType(state, binding.index);
|
||||||
|
}
|
||||||
binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput,
|
binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput,
|
||||||
binding.debug_name.c_str());
|
binding.debug_name.c_str());
|
||||||
const bool dual_source = binding.kind == IR::StageOutputKind::Mrt &&
|
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.object_primitive_id));
|
||||||
key.push_back(static_cast<uint32_t>(info.alpha_blend_source));
|
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));
|
||||||
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;
|
const auto mask = info.target_shader_mask;
|
||||||
key.push_back((mask | (mask >> 1u) | (mask >> 2u) | (mask >> 3u)) & 0x11111111u);
|
key.push_back((mask | (mask >> 1u) | (mask >> 2u) | (mask >> 3u)) & 0x11111111u);
|
||||||
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) {
|
||||||
|
|||||||
@@ -181,6 +181,9 @@ struct ShaderPixelInputInfo {
|
|||||||
uint32_t target_shader_mask = UINT32_MAX;
|
uint32_t target_shader_mask = UINT32_MAX;
|
||||||
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
||||||
uint8_t target_packed_unorm_mask = 0;
|
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.
|
// CB round mode per converted export: zero adds half an LSB, one truncates.
|
||||||
uint8_t target_round_mode = 0;
|
uint8_t target_round_mode = 0;
|
||||||
uint32_t scratch_size_dwords = 0;
|
uint32_t scratch_size_dwords = 0;
|
||||||
|
|||||||
@@ -19808,6 +19808,40 @@ public:
|
|||||||
}
|
}
|
||||||
Require(name, "packed rounding program distinction", half_up_program != truncate_program,
|
Require(name, "packed rounding program distinction", half_up_program != truncate_program,
|
||||||
"half-up and truncate exports shared one shader specialization");
|
"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);
|
DestroyBuffer(&packed_output);
|
||||||
m_device.destroySampler(packed_sampler);
|
m_device.destroySampler(packed_sampler);
|
||||||
DestroyBuffer(&stencil_readback);
|
DestroyBuffer(&stencil_readback);
|
||||||
|
|||||||
@@ -13748,6 +13748,7 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
|||||||
|
|
||||||
ShaderPixelInputInfo uint16_info;
|
ShaderPixelInputInfo uint16_info;
|
||||||
uint16_info.target_output_mode[0] = 7;
|
uint16_info.target_output_mode[0] = 7;
|
||||||
|
uint16_info.target_uint_mask = 1;
|
||||||
options.input_info.pixel = &uint16_info;
|
options.input_info.pixel = &uint16_info;
|
||||||
auto uint16_result = RecompileForTest(shader, options);
|
auto uint16_result = RecompileForTest(shader, options);
|
||||||
const auto uint16_source = DisassembleSpirvBinary(uint16_result.spirv);
|
const auto uint16_source = DisassembleSpirvBinary(uint16_result.spirv);
|
||||||
@@ -13762,6 +13763,18 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
|||||||
"compressed UINT16 MRT export was incorrectly decoded as FP16");
|
"compressed UINT16 MRT export was incorrectly decoded as FP16");
|
||||||
CheckSpirvBinaryValidates(uint16_result.spirv);
|
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;
|
ShaderPixelInputInfo unorm16_info;
|
||||||
unorm16_info.target_output_mode[0] = 5;
|
unorm16_info.target_output_mode[0] = 5;
|
||||||
options.input_info.pixel = &unorm16_info;
|
options.input_info.pixel = &unorm16_info;
|
||||||
@@ -13789,6 +13802,7 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
|||||||
|
|
||||||
ShaderPixelInputInfo uint_info;
|
ShaderPixelInputInfo uint_info;
|
||||||
uint_info.target_output_mode[0] = 7;
|
uint_info.target_output_mode[0] = 7;
|
||||||
|
uint_info.target_uint_mask = 1;
|
||||||
options.input_info.pixel = &uint_info;
|
options.input_info.pixel = &uint_info;
|
||||||
auto partial_uint_result =
|
auto partial_uint_result =
|
||||||
RecompileForTest(partial_shader, options);
|
RecompileForTest(partial_shader, options);
|
||||||
@@ -13815,6 +13829,15 @@ void TestNewShaderRecompilerExpPixelOutputs() {
|
|||||||
"compressed UINT16 BA-only export did not read and unpack VSRC1");
|
"compressed UINT16 BA-only export did not read and unpack VSRC1");
|
||||||
CheckSpirvBinaryValidates(compressed_ba_result.spirv);
|
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;
|
unorm16_info.target_output_mode[0] = 5;
|
||||||
options.input_info.pixel = &unorm16_info;
|
options.input_info.pixel = &unorm16_info;
|
||||||
auto unorm16_ba_result =
|
auto unorm16_ba_result =
|
||||||
|
|||||||
Reference in New Issue
Block a user