mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 20:06:54 +02:00
shader: preserve EXEC witnesses across reconverged descriptor loops
This commit is contained in:
@@ -1063,19 +1063,35 @@ private:
|
|||||||
return edge && edge->positive ? edge->condition : Value {};
|
return edge && edge->positive ? edge->condition : Value {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GuardedOnEntry(const Block* block, const auto& stops, const auto& accepts) const {
|
||||||
|
std::vector<const Block*> pending {block};
|
||||||
|
for (size_t i = 0; i < pending.size(); ++i) {
|
||||||
|
const auto* current = pending[i];
|
||||||
|
if (stops(current) || current->ImmPredecessors().empty()) return false;
|
||||||
|
for (const auto* previous: current->ImmPredecessors()) {
|
||||||
|
const auto edge = ConditionalEdge(previous, current);
|
||||||
|
if (edge && accepts(*edge)) continue;
|
||||||
|
if (std::ranges::find(pending, previous) == pending.end())
|
||||||
|
pending.push_back(previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasActiveLane(Value mask, const Block* use) const {
|
bool HasActiveLane(Value mask, const Block* use) const {
|
||||||
mask = mask.Resolve();
|
mask = mask.Resolve();
|
||||||
const auto incoming_is_nonempty = [&](const Block* from, const Block* to,
|
const auto incoming_is_nonempty = [&](const Block* from, const Block* to,
|
||||||
Value incoming, const Block* header) {
|
Value incoming, const Block* header) {
|
||||||
for (size_t depth = 0; depth < m_program.blocks.size(); ++depth) {
|
const auto accepts = [&](const EdgePredicate& edge) {
|
||||||
const auto edge = ConditionalEdge(from, to);
|
return edge.positive && Implies(edge.condition, incoming);
|
||||||
if (edge && edge->positive && Implies(edge->condition, incoming)) return true;
|
};
|
||||||
if (from == header || from->ImmSuccessors().size() != 1u ||
|
const auto edge = ConditionalEdge(from, to);
|
||||||
from->ImmPredecessors().size() != 1u) return false;
|
if (edge && accepts(*edge)) return true;
|
||||||
to = from;
|
const auto* definition = incoming.Resolve().TryInstruction();
|
||||||
from = from->ImmPredecessors()[0];
|
return GuardedOnEntry(from, [&](const Block* block) {
|
||||||
}
|
// A witness before this definition may belong to an earlier loop iteration.
|
||||||
return false;
|
return block == header || (definition != nullptr && block == definition->Parent());
|
||||||
|
}, accepts);
|
||||||
};
|
};
|
||||||
const auto* phi = mask.TryInstruction();
|
const auto* phi = mask.TryInstruction();
|
||||||
if (phi != nullptr && phi->GetOpcode() == ValueOpcode::Phi &&
|
if (phi != nullptr && phi->GetOpcode() == ValueOpcode::Phi &&
|
||||||
@@ -1401,32 +1417,19 @@ private:
|
|||||||
}
|
}
|
||||||
if (increment_block == nullptr) return {};
|
if (increment_block == nullptr) return {};
|
||||||
|
|
||||||
const auto guarded_on_entry = [&](const Block* block, const auto& accepts) {
|
const auto stops = [&](const Block* block) { return block == phi->Parent(); };
|
||||||
std::vector<const Block*> pending {block};
|
|
||||||
for (size_t i = 0; i < pending.size(); ++i) {
|
|
||||||
const auto* current = pending[i];
|
|
||||||
if (current == phi->Parent() || current->ImmPredecessors().empty()) return false;
|
|
||||||
for (const auto* previous: current->ImmPredecessors()) {
|
|
||||||
const auto edge = ConditionalEdge(previous, current);
|
|
||||||
if (edge && accepts(*edge)) continue;
|
|
||||||
if (std::ranges::find(pending, previous) == pending.end())
|
|
||||||
pending.push_back(previous);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
for (const auto& use_of_key: phi->Uses()) {
|
for (const auto& use_of_key: phi->Uses()) {
|
||||||
const auto* compare = use_of_key.user;
|
const auto* compare = use_of_key.user;
|
||||||
if ((compare->GetOpcode() != ValueOpcode::SLessThan32 &&
|
if ((compare->GetOpcode() != ValueOpcode::SLessThan32 &&
|
||||||
compare->GetOpcode() != ValueOpcode::ULessThan32) || use_of_key.operand != 0u ||
|
compare->GetOpcode() != ValueOpcode::ULessThan32) || use_of_key.operand != 0u ||
|
||||||
!ValidateRuntimeValue(m_program, compare->Arg(1), RuntimeValueType::Integer)) continue;
|
!ValidateRuntimeValue(m_program, compare->Arg(1), RuntimeValueType::Integer)) continue;
|
||||||
if (!guarded_on_entry(use, [&](const EdgePredicate& edge) {
|
if (!GuardedOnEntry(use, stops, [&](const EdgePredicate& edge) {
|
||||||
return edge.positive && Implies(edge.condition, Value(use_of_key.user));
|
return edge.positive && Implies(edge.condition, Value(use_of_key.user));
|
||||||
})) continue;
|
})) continue;
|
||||||
LoopBoundProof proof(m_program, *phi, *compare);
|
LoopBoundProof proof(m_program, *phi, *compare);
|
||||||
// The image bound and the increment guard are separate obligations: a
|
// The image bound and the increment guard are separate obligations: a
|
||||||
// skipped image alone does not prevent signed induction wraparound.
|
// skipped image alone does not prevent signed induction wraparound.
|
||||||
if (guarded_on_entry(increment_block, [&](const EdgePredicate& edge) {
|
if (GuardedOnEntry(increment_block, stops, [&](const EdgePredicate& edge) {
|
||||||
return proof.Excludes(edge.condition, edge.positive);
|
return proof.Excludes(edge.condition, edge.positive);
|
||||||
})) return compare;
|
})) return compare;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35614,13 +35614,28 @@ void CheckImageSamplerSpecialization() {
|
|||||||
user_data[48] = 0x3000u;
|
user_data[48] = 0x3000u;
|
||||||
std::vector<u32> code{EncodeVopc(0xc5, InlineU32(0), 0),
|
std::vector<u32> code{EncodeVopc(0xc5, InlineU32(0), 0),
|
||||||
EncodeSop1(0x24, 24, 106), EncodeSopp(0x08, 0),
|
EncodeSop1(0x24, 24, 106), EncodeSopp(0x08, 0),
|
||||||
EncodeMubuf0(0x0c, 0, true, false), EncodeMubuf1(15, 1, 0),
|
EncodeSop1(0x04, 26, 126), EncodeVop1(0x01, 16, InlineU32(1))};
|
||||||
EncodeVop2(0x16, 15, InlineU32(4), 15),
|
// An earlier descriptor waterfall separates the EXECZ witness from this material load.
|
||||||
EncodeVop1(0x02, 20, Vgpr(15)), EncodeVopc(0xd2, 20, 15),
|
const auto previous_loop = code.size();
|
||||||
EncodeSop2(0x1e, 20, 20, InlineU32(5)),
|
code.insert(code.end(), {EncodeSop1(0x04, 28, 126),
|
||||||
EncodeSmem0(0x0b, 12), EncodeSmem1(0, 20),
|
EncodeVop1(0x02, 22, Vgpr(0)), EncodeVopc(0xd2, 22, 0),
|
||||||
EncodeMimg0(0x24, 1), EncodeMimg1(4, 0, 3, 2)};
|
EncodeVop1(0x01, 16, InlineU32(0)),
|
||||||
|
EncodeSop1(0x04, 126, 28), EncodeVopc(0xd5, InlineU32(0), 16)});
|
||||||
|
code.push_back(EncodeSopp(0x09, static_cast<int16_t>(previous_loop - code.size() - 1u)));
|
||||||
|
code.insert(code.end(), {EncodeSop1(0x04, 126, 26),
|
||||||
|
EncodeMubuf0(0x0c, 0, true, false), EncodeMubuf1(15, 1, 0),
|
||||||
|
EncodeVop2(0x16, 15, InlineU32(4), 15),
|
||||||
|
EncodeVop1(0x01, 16, InlineU32(1))});
|
||||||
|
const auto image_loop = code.size();
|
||||||
|
code.insert(code.end(), {EncodeSop1(0x04, 28, 126),
|
||||||
|
EncodeVop1(0x02, 20, Vgpr(15)), EncodeVopc(0xd2, 20, 15),
|
||||||
|
EncodeSop2(0x1e, 20, 20, InlineU32(5)),
|
||||||
|
EncodeSmem0(0x0b, 12), EncodeSmem1(0, 20),
|
||||||
|
EncodeMimg0(0x24, 1), EncodeMimg1(4, 0, 3, 2)});
|
||||||
AppendBufferStoreDword(&code, 4, 0);
|
AppendBufferStoreDword(&code, 4, 0);
|
||||||
|
code.insert(code.end(), {EncodeVop1(0x01, 16, InlineU32(0)),
|
||||||
|
EncodeSop1(0x04, 126, 28), EncodeVopc(0xd5, InlineU32(0), 16)});
|
||||||
|
code.push_back(EncodeSopp(0x09, static_cast<int16_t>(image_loop - code.size() - 1u)));
|
||||||
code[2] = EncodeSopp(0x08, static_cast<int16_t>(code.size() - 3u));
|
code[2] = EncodeSopp(0x08, static_cast<int16_t>(code.size() - 3u));
|
||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
ShaderComputeInputInfo compute{};
|
ShaderComputeInputInfo compute{};
|
||||||
@@ -35675,6 +35690,12 @@ void CheckImageSamplerSpecialization() {
|
|||||||
Require(name, "dirty material rejection",
|
Require(name, "dirty material rejection",
|
||||||
!MaterializeResources(plan, dirty, snapshot, specialization),
|
!MaterializeResources(plan, dirty, snapshot, specialization),
|
||||||
"GPU-selected texture materialization bypassed strict read provenance");
|
"GPU-selected texture materialization bypassed strict read provenance");
|
||||||
|
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||||
|
ExpectFatal("MaterialImageWithoutEntryWitness", [&] {
|
||||||
|
code[2] = EncodeSopp(0x00, 0); // S_NOP removes the original nonempty-EXEC witness.
|
||||||
|
(void)ShaderRecompiler::TranslateProgram(code, options);
|
||||||
|
});
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::printf("[host] %-32s ok\n", "ImageSpecializationPipelineId");
|
std::printf("[host] %-32s ok\n", "ImageSpecializationPipelineId");
|
||||||
|
|||||||
Reference in New Issue
Block a user