Compare commits

..

1 Commits

Author SHA1 Message Date
xbzk 4b7570b24d [ir] resolve conditional Phi inputs when tracking bindless texture handles 2026-10-18 01:19:33 -03:00
2 changed files with 79 additions and 19 deletions
@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <functional>
#include <optional> #include <optional>
#include <type_traits> #include <type_traits>
#include <queue> #include <queue>
@@ -13,9 +17,10 @@
namespace Shader::IR { namespace Shader::IR {
template <typename Pred> template <typename Pred, typename Resolve = std::identity>
auto BreadthFirstSearch(const Value& value, Pred&& pred) auto BreadthFirstSearch(Value value, Pred&& pred, Resolve&& resolve = {})
-> std::invoke_result_t<Pred, const Inst*> { -> std::invoke_result_t<Pred, const Inst*> {
value = resolve(value);
if (value.IsImmediate()) { if (value.IsImmediate()) {
// Nothing to do with immediates // Nothing to do with immediates
return std::nullopt; return std::nullopt;
@@ -36,7 +41,7 @@ auto BreadthFirstSearch(const Value& value, Pred&& pred)
} }
// Visit the right most arguments first // Visit the right most arguments first
for (size_t arg = inst->NumArgs(); arg--;) { for (size_t arg = inst->NumArgs(); arg--;) {
const Value arg_value{inst->Arg(arg)}; const Value arg_value{resolve(inst->Arg(arg))};
if (arg_value.IsImmediate()) { if (arg_value.IsImmediate()) {
continue; continue;
} }
+71 -16
View File
@@ -31,6 +31,60 @@ struct TextureInst {
using TextureInstVector = boost::container::small_vector<TextureInst, 24>; using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
const IR::Block* ConditionalHeader(const IR::Block* body) {
const auto predecessors{body->ImmPredecessors()};
const auto successors{body->ImmSuccessors()};
if (predecessors.size() != 1 || successors.size() != 1) return nullptr;
const IR::Block* const header{predecessors[0]};
const auto targets{header->ImmSuccessors()};
if (targets.size() != 2 || targets[0] != body || targets[1] != successors[0] ||
targets[1]->ImmPredecessors().size() != 2 || header->empty() ||
header->back().GetOpcode() != IR::Opcode::ConditionRef) {
return nullptr;
}
return header;
}
std::pair<IR::Value, bool> Condition(IR::Value value) {
bool negated{};
while (const IR::Inst* inst = value.TryInstRecursive()) {
if (inst->GetOpcode() == IR::Opcode::LogicalNot) negated = !negated;
else if (inst->GetOpcode() != IR::Opcode::ConditionRef) break;
value = inst->Arg(0);
}
return {value.Resolve(), negated};
}
IR::Value ResolveConditionalPhi(IR::Value value, const IR::Block* block) {
if (!value.Resolve().IsPhi() || !(block = ConditionalHeader(block))) return value;
const auto [condition, negated]{Condition(block->back().Arg(0))};
boost::container::small_vector<const IR::Block*, 16> visited;
while (value.Resolve().IsPhi() && std::ranges::find(visited, block) == visited.end()) {
visited.push_back(block);
const IR::Inst* const phi{value.InstRecursive()};
const auto predecessors{block->ImmPredecessors()};
if (predecessors.size() == 1) {
block = predecessors[0];
continue;
}
if (predecessors.size() != 2 || phi->NumArgs() != 2) break;
const IR::Block* body{predecessors[0]};
const IR::Block* header{ConditionalHeader(body)};
if (header != predecessors[1]) {
body = predecessors[1];
header = ConditionalHeader(body);
if (header != predecessors[0]) break;
}
const auto [other_condition, other_negated]{Condition(header->back().Arg(0))};
if (condition == other_condition && (phi->PhiBlock(0) == header || phi->PhiBlock(0) == body)) {
const IR::Block* const incoming{negated == other_negated ? body : header};
value = phi->Arg(phi->PhiBlock(0) == incoming ? 0 : 1);
}
block = header;
}
return value;
}
constexpr u32 DESCRIPTOR_SIZE = 8; constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE)); constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024; constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
@@ -299,23 +353,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
} }
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info); std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) { static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
if (const IR::Inst* key = v.InstRecursive()) { if (const IR::Inst* key = v.TryInstRecursive()) {
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second; if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
auto found = Track(v, env, host_info); auto found = Track(v, env, host_info, block);
if (found) env.track_cache.emplace(key, *found); if (found) env.track_cache.emplace(key, *found);
return found; return found;
} }
return Track(v, env, host_info); return Track(v, env, host_info, block);
} }
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info); std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block);
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) { std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) { return IR::BreadthFirstSearch(value, [&env, &host_info, block](const IR::Inst* inst) {
return TryGetConstBuffer(inst, env, host_info); return TryGetConstBuffer(inst, env, host_info, block);
}); }, [&](IR::Value operand) { return ResolveConditionalPhi(operand, block); });
} }
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) { std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
@@ -339,13 +393,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
return ReadCbufCached(env, index_number, offset_number); return ReadCbufCached(env, index_number, offset_number);
} }
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) { std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
switch (inst->GetOpcode()) { switch (inst->GetOpcode()) {
default: default:
return std::nullopt; return std::nullopt;
case IR::Opcode::BitwiseOr32: { case IR::Opcode::BitwiseOr32: {
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)}; std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)};
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)}; std::optional rhs{TrackCached(inst->Arg(1), env, host_info, block)};
if (!lhs || !rhs) { if (!lhs || !rhs) {
return std::nullopt; return std::nullopt;
} }
@@ -375,7 +429,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
if (!shift.IsImmediate()) { if (!shift.IsImmediate()) {
return std::nullopt; return std::nullopt;
} }
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)}; std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)};
if (lhs) { if (lhs) {
lhs->shift_left = shift.U32(); lhs->shift_left = shift.U32();
} }
@@ -403,7 +457,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
return std::nullopt; return std::nullopt;
} while (false); } while (false);
} }
std::optional lhs{TrackCached(op1, env, host_info)}; std::optional lhs{TrackCached(op1, env, host_info, block)};
if (lhs) { if (lhs) {
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32())); lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
} }
@@ -469,7 +523,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) { TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
ConstBufferAddr addr; ConstBufferAddr addr;
if (IsBindless(inst)) { if (IsBindless(inst)) {
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)}; const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info, block)};
if (!track_addr) { if (!track_addr) {
throw NotImplementedException("Failed to track bindless texture constant buffer"); throw NotImplementedException("Failed to track bindless texture constant buffer");
@@ -671,6 +725,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
env.track_cache.clear(); env.track_cache.clear();
TextureInstVector to_replace; TextureInstVector to_replace;
for (IR::Block* const block : program.post_order_blocks) { for (IR::Block* const block : program.post_order_blocks) {
env.track_cache.clear();
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
if (!IsTextureInstruction(inst)) { if (!IsTextureInstruction(inst)) {
continue; continue;