mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
graphics: route compact color exports through CB_SHADER_MASK
This commit is contained in:
@@ -589,8 +589,12 @@ static uint32_t DrawColorOutputMask(const HW::Context& ctx) {
|
||||
const auto& sh_regs = ctx.GetShaderRegisters();
|
||||
const auto write_mask = ctx.GetRenderTargetMask() & sh_regs.m_cbShaderMask;
|
||||
uint32_t output_mask = 0;
|
||||
for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) {
|
||||
if (sh_regs.target_output_mode[slot] != 0 &&
|
||||
for (uint32_t index = 0; index < RENDER_COLOR_ATTACHMENTS_MAX; index++) {
|
||||
const auto slot = ShaderPixelExportTarget(sh_regs.m_cbShaderMask, index);
|
||||
if (slot >= RENDER_COLOR_ATTACHMENTS_MAX) {
|
||||
break;
|
||||
}
|
||||
if (sh_regs.target_output_mode[index] != 0 &&
|
||||
render_target_mask_slot(write_mask, slot) != 0) {
|
||||
output_mask |= 1u << slot;
|
||||
}
|
||||
@@ -902,7 +906,7 @@ bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCal
|
||||
if (state.ps_active) {
|
||||
for (const auto& output: state.ps_input_info.stage.program->info.outputs) {
|
||||
if (output.kind == ShaderRecompiler::IR::StageOutputKind::Mrt) {
|
||||
mrt_mask |= 1u << output.index;
|
||||
mrt_mask |= 1u << output.location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,16 +390,23 @@ void CollectOutputs(const Program& program, ShaderStageInputInfo input_info, Sha
|
||||
AddOutput(info, StageOutputKind::Parameter, export_info.index,
|
||||
export_info.index, fmt::format("out_param_{}", export_info.index));
|
||||
break;
|
||||
case ExportTargetKind::Mrt:
|
||||
case ExportTargetKind::Mrt: {
|
||||
if (alpha_remap && export_info.index != 0) {
|
||||
break;
|
||||
}
|
||||
AddOutput(info, StageOutputKind::Mrt, export_info.index, export_info.index,
|
||||
const auto slot = input_info.pixel->dual_source_blending
|
||||
? 0u : ShaderPixelExportTarget(input_info.pixel->target_shader_mask,
|
||||
export_info.index);
|
||||
if (slot >= 8) {
|
||||
break;
|
||||
}
|
||||
AddOutput(info, StageOutputKind::Mrt, export_info.index, slot,
|
||||
fmt::format("out_mrt_{}", export_info.index));
|
||||
if (alpha_remap) {
|
||||
AddOutput(info, StageOutputKind::Mrt, 1, 1, "out_mrt_1");
|
||||
AddOutput(info, StageOutputKind::Mrt, 1, 0, "out_mrt_1");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,10 +561,12 @@ static void ShaderGetStaticInputInfoPS(
|
||||
ps_info.interpolator_settings[i] = sh.ps_interpolator_settings[i];
|
||||
}
|
||||
|
||||
ps_info.target_shader_mask = sh.m_cbShaderMask;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const auto slot = ShaderPixelExportTarget(ps_info.target_shader_mask, i);
|
||||
ps_info.target_output_mode[i] = sh.target_output_mode[i];
|
||||
ps_info.target_export_mapping[i] = sh.target_output_mode[i] != 0
|
||||
? target_export_mapping[i]
|
||||
ps_info.target_export_mapping[i] = sh.target_output_mode[i] != 0 && slot < 8
|
||||
? target_export_mapping[slot]
|
||||
: Prospero::ColorComponentMapping {};
|
||||
}
|
||||
}
|
||||
@@ -668,6 +670,8 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
||||
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
|
||||
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));
|
||||
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) {
|
||||
uint32_t packed = 0;
|
||||
for (uint32_t i = 0; i < 4u; i++) {
|
||||
|
||||
@@ -170,6 +170,7 @@ struct ShaderPixelInputInfo {
|
||||
uint32_t ps_perspective_sample_vgpr = UINT32_MAX;
|
||||
uint32_t ps_perspective_centroid_vgpr = UINT32_MAX;
|
||||
uint8_t target_output_mode[8] = {};
|
||||
uint32_t target_shader_mask = UINT32_MAX;
|
||||
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
||||
uint32_t scratch_size_dwords = 0;
|
||||
bool ps_pos_x = false;
|
||||
@@ -310,6 +311,7 @@ struct ShaderMappedData {
|
||||
|
||||
void ShaderInit();
|
||||
void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data);
|
||||
uint32_t ShaderPixelExportTarget(uint32_t shader_mask, uint32_t export_index);
|
||||
|
||||
void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info);
|
||||
void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info);
|
||||
|
||||
@@ -11,6 +11,19 @@ constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
||||
|
||||
} // namespace
|
||||
|
||||
uint32_t ShaderPixelExportTarget(uint32_t shader_mask, uint32_t export_index) {
|
||||
for (uint32_t target = 0; target < 8u; target++) {
|
||||
if (((shader_mask >> (target * 4u)) & 0xfu) == 0u) {
|
||||
continue;
|
||||
}
|
||||
if (export_index == 0u) {
|
||||
return target;
|
||||
}
|
||||
export_index--;
|
||||
}
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input) {
|
||||
return input < info.input_num ? info.interpolator_settings[input] & PsInputOffsetMask : input;
|
||||
}
|
||||
|
||||
@@ -15610,6 +15610,7 @@ public:
|
||||
HW::UserConfig user_config{};
|
||||
HW::Shader shaders{};
|
||||
registers.SetRenderTargetMask(0xf);
|
||||
registers.SetShaderMask(0xf);
|
||||
scheduler.Begin(registers, user_config, shaders);
|
||||
auto &resources = context;
|
||||
auto &cache = context.GetTextureCache();
|
||||
@@ -16353,8 +16354,9 @@ public:
|
||||
registers.SetColorAttrib2(slot, {.height = side - 1, .width = side - 1});
|
||||
registers.SetColorAttrib3(slot,
|
||||
{.tile_mode = Prospero::TileMode::kLinear, .dimension = 1});
|
||||
registers.SetTargetOutputMode(slot, 4);
|
||||
}
|
||||
registers.SetTargetOutputMode(0, 4);
|
||||
registers.SetTargetOutputMode(1, 4);
|
||||
stencil_target.z_info.htile_acceleration = true;
|
||||
stencil_target.htile_data_base_addr = depth_address + 0x30000;
|
||||
registers.SetDepthRenderTarget(stencil_target);
|
||||
@@ -16416,7 +16418,7 @@ public:
|
||||
registers.SetDepthShaderControl({});
|
||||
registers.SetDepthControl({.z_enable = true, .z_write_enable = true,
|
||||
.zfunc = static_cast<uint8_t>(vk::CompareOp::eAlways)});
|
||||
for (const auto slot : {0u, 3u}) {
|
||||
for (const auto slot : {0u, 1u}) {
|
||||
registers.SetTargetOutputMode(slot, 0);
|
||||
}
|
||||
RenderExecutorTestAccess::DrawAuto(
|
||||
@@ -16427,37 +16429,48 @@ public:
|
||||
std::ranges::all_of(depth_only_pixels, [](u32 v) { return v == 0; }),
|
||||
"disabled color exports invoked the stale PS or clipped fixed-function depth");
|
||||
registers.SetPsInControl(0x8000);
|
||||
for (const auto slot : {0u, 3u}) {
|
||||
registers.SetTargetOutputMode(slot, 4);
|
||||
}
|
||||
registers.SetTargetOutputMode(0, 4);
|
||||
|
||||
// With MRT0 still bound, an MRT3-only export must retain location3 in
|
||||
// rendering attachments, pipeline formats, blend masks and dynamic write enables.
|
||||
static const auto sparse_pixel = [] {
|
||||
auto code = native_pixel;
|
||||
*std::ranges::find(code, EncodeExp0(0x00, 0xf)) = EncodeExp0(0x03, 0xf);
|
||||
return code;
|
||||
// Export ordinals follow CB_SHADER_MASK. A disabled CB_TARGET_MASK slot
|
||||
// still consumes an ordinal, and stale MRT0 must not clip physical MRT3.
|
||||
static const auto sparse_pixels_code = [] {
|
||||
std::array<std::vector<u32>, 2> result{native_pixel, native_pixel};
|
||||
auto &code = result[1];
|
||||
const auto exp = std::ranges::find(code, EncodeExp0(0, 0xf));
|
||||
*exp = EncodeExp0(0, 0xf, false);
|
||||
*(exp + 1) = EncodeExp1(0, 0, 0, 0);
|
||||
code.insert(exp + 2, {EncodeExp0(1, 0xf), EncodeExp1(1, 1, 1, 2)});
|
||||
return result;
|
||||
}();
|
||||
const auto sparse_address = reinterpret_cast<uint64_t>(sparse_pixel.data());
|
||||
ShaderMapUserData(sparse_address,
|
||||
{.type = Prospero::ShaderBinaryType::kPs,
|
||||
.code_size_bytes = static_cast<uint32_t>(sparse_pixel.size() * sizeof(u32))});
|
||||
shaders.SetPsShaderBase(sparse_address);
|
||||
registers.SetRenderTargetMask(0xf000);
|
||||
registers.SetDepthControl({});
|
||||
registers.SetDepthShaderControl({});
|
||||
RenderExecutorTestAccess::DrawAuto(
|
||||
executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1});
|
||||
RenderColorInfo sparse_color{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, scheduler.Current(), sparse_color, 3);
|
||||
const auto sparse_pixels = ReadCachedTexel(name, context, sparse_color.image_id,
|
||||
{}, {extent, extent, 1});
|
||||
for (size_t component = 0; component < sparse_pixels.size(); component++) {
|
||||
const auto expected = component % 4 == 3 ? 0x3f800000u : 0x3e800000u;
|
||||
Require(name, "sparse MRT3 output", sparse_pixels[component] == expected,
|
||||
"MRT3 was compacted to a different slot or clipped by unused MRT0");
|
||||
for (uint32_t index = 0; index < sparse_pixels_code.size(); index++) {
|
||||
const auto &code = sparse_pixels_code[index];
|
||||
const auto address = reinterpret_cast<uint64_t>(code.data());
|
||||
ShaderMapUserData(address,
|
||||
{.type = Prospero::ShaderBinaryType::kPs,
|
||||
.code_size_bytes = static_cast<uint32_t>(code.size() * sizeof(u32))});
|
||||
shaders.SetPsShaderBase(address);
|
||||
registers.SetShaderMask(index == 0 ? 0xf000 : 0xf00f);
|
||||
registers.SetTargetOutputMode(1, index == 0 ? 0 : 4);
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, scheduler.Current(), sparse_color, 3);
|
||||
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), sparse_color.image_id,
|
||||
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, {});
|
||||
RenderExecutorTestAccess::DrawAuto(
|
||||
executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1});
|
||||
const auto pixels = ReadCachedTexel(name, context, sparse_color.image_id,
|
||||
{}, {extent, extent, 1});
|
||||
for (size_t component = 0; component < pixels.size(); component++) {
|
||||
const auto expected = component % 4 == 3 ? 0x3f800000u : 0x3e800000u;
|
||||
Require(name, "compact export to sparse MRT3", pixels[component] == expected,
|
||||
"shader output routing used the write mask or clipped MRT3 with stale MRT0");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// A fourth VS invocation reads beyond the descriptor instead of reconstructing the corner.
|
||||
const ShaderBufferResource rect_buffer{{
|
||||
static_cast<u32>(rect_address),
|
||||
@@ -16483,7 +16496,7 @@ public:
|
||||
}
|
||||
AppendVMovU32(&code, 22, 0);
|
||||
AppendVMovLiteral(&code, 23, 0x3f800000u);
|
||||
code.insert(code.end(), {EncodeExp0(0x03, 0xf), EncodeExp1(20, 21, 22, 23)});
|
||||
code.insert(code.end(), {EncodeExp0(0, 0xf), EncodeExp1(20, 21, 22, 23)});
|
||||
AppendEnd(&code);
|
||||
return code;
|
||||
}();
|
||||
|
||||
@@ -13023,9 +13023,23 @@ void TestRenderTargetReverseExportMapping() {
|
||||
"inactive reverse MRT mapping was not normalized out of the shader "
|
||||
"cache key");
|
||||
sh.target_output_mode[0] = 4;
|
||||
sh.m_cbShaderMask = 0xf;
|
||||
PrepareProgram(regs, sh, mappings, compiled_info);
|
||||
Check(compiled_info.target_export_mapping[0] == gr32.export_mapping,
|
||||
"active reverse MRT mapping was lost before shader specialization");
|
||||
const auto first_slot_key = MakeStageStaticKey(compiled_info);
|
||||
sh.m_cbShaderMask = 0xf000;
|
||||
mappings[3] = gr32.export_mapping;
|
||||
PrepareProgram(regs, sh, mappings, compiled_info);
|
||||
Check(compiled_info.target_shader_mask == sh.m_cbShaderMask &&
|
||||
compiled_info.target_export_mapping[0] == gr32.export_mapping &&
|
||||
first_slot_key != MakeStageStaticKey(compiled_info),
|
||||
"compact MRT ordinal lost its physical export mapping or cache identity");
|
||||
const auto sparse_slot_key = MakeStageStaticKey(compiled_info);
|
||||
sh.m_cbShaderMask = 0x1000;
|
||||
PrepareProgram(regs, sh, mappings, compiled_info);
|
||||
Check(sparse_slot_key == MakeStageStaticKey(compiled_info),
|
||||
"component-only shader mask changes introduced a duplicate shader variant");
|
||||
}
|
||||
|
||||
void TestBlendMappingClassification() {
|
||||
|
||||
Reference in New Issue
Block a user