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