mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-06 14:09:58 +00:00
Just force reads on 8/16bits
This commit is contained in:
@@ -962,7 +962,7 @@ void EmitContext::DefineGlobalMemoryFunctions(const IR::Program& program) {
|
||||
OpBranchConditional(cond, then_label, else_label);
|
||||
AddLabel(then_label);
|
||||
const Id word{OpShiftRightLogical(U32[1], offset, Const(2U))};
|
||||
const Id result{callback(ssbos[index].U32, word, offset, value)};
|
||||
const Id result{callback(ssbos[index], word, offset, value)};
|
||||
if (returns_value) {
|
||||
OpReturnValue(result);
|
||||
} else {
|
||||
@@ -978,11 +978,22 @@ void EmitContext::DefineGlobalMemoryFunctions(const IR::Program& program) {
|
||||
OpFunctionEnd();
|
||||
global_memory_funcs.emplace(opcode, func);
|
||||
}};
|
||||
const auto vector_pointer{[&](const StorageDefinitions& ssbo, Id word, u32 count) {
|
||||
if (count == 2) {
|
||||
return OpAccessChain(storage_types.U32x2.element, ssbo.U32x2, zero,
|
||||
OpShiftRightLogical(U32[1], word, Const(1U)));
|
||||
}
|
||||
return OpAccessChain(storage_types.U32x4.element, ssbo.U32x4, zero,
|
||||
OpShiftRightLogical(U32[1], word, Const(2U)));
|
||||
}};
|
||||
const auto load{[&](Id type, u32 count) {
|
||||
return [&, type, count](Id ssbo, Id word, Id, Id) {
|
||||
return [&, type, count](const StorageDefinitions& ssbo, Id word, Id, Id) {
|
||||
if (count > 1 && profile.support_descriptor_aliasing) {
|
||||
return OpLoad(type, vector_pointer(ssbo, word, count));
|
||||
}
|
||||
std::array<Id, 4> words{};
|
||||
for (u32 element = 0; element < count; ++element) {
|
||||
words[element] = OpLoad(U32[1], word_pointer(ssbo, word, element));
|
||||
words[element] = OpLoad(U32[1], word_pointer(ssbo.U32, word, element));
|
||||
}
|
||||
if (count == 1) {
|
||||
return words[0];
|
||||
@@ -991,21 +1002,25 @@ void EmitContext::DefineGlobalMemoryFunctions(const IR::Program& program) {
|
||||
};
|
||||
}};
|
||||
const auto store{[&](u32 count) {
|
||||
return [&, count](Id ssbo, Id word, Id, Id value) {
|
||||
return [&, count](const StorageDefinitions& ssbo, Id word, Id, Id value) {
|
||||
if (count > 1 && profile.support_descriptor_aliasing) {
|
||||
OpStore(vector_pointer(ssbo, word, count), value);
|
||||
return Id{};
|
||||
}
|
||||
if (count == 1) {
|
||||
OpStore(word_pointer(ssbo, word, 0), value);
|
||||
OpStore(word_pointer(ssbo.U32, word, 0), value);
|
||||
return Id{};
|
||||
}
|
||||
for (u32 element = 0; element < count; ++element) {
|
||||
OpStore(word_pointer(ssbo, word, element),
|
||||
OpStore(word_pointer(ssbo.U32, word, element),
|
||||
OpCompositeExtract(U32[1], value, element));
|
||||
}
|
||||
return Id{};
|
||||
};
|
||||
}};
|
||||
const auto extract{[&](bool is_signed, u32 count) {
|
||||
return [&, is_signed, count](Id ssbo, Id word, Id offset, Id) {
|
||||
const Id loaded{OpLoad(U32[1], word_pointer(ssbo, word, 0))};
|
||||
return [&, is_signed, count](const StorageDefinitions& ssbo, Id word, Id offset, Id) {
|
||||
const Id loaded{OpLoad(U32[1], word_pointer(ssbo.U32, word, 0))};
|
||||
if (is_signed) {
|
||||
return OpBitFieldSExtract(U32[1], loaded, bits(offset, count), Const(count));
|
||||
}
|
||||
@@ -1013,28 +1028,28 @@ void EmitContext::DefineGlobalMemoryFunctions(const IR::Program& program) {
|
||||
};
|
||||
}};
|
||||
const auto insert{[&](u32 count) {
|
||||
return [&, count](Id ssbo, Id word, Id offset, Id value) {
|
||||
AtomicBitFieldInsert(word_pointer(ssbo, word, 0), value, bits(offset, count),
|
||||
return [&, count](const StorageDefinitions& ssbo, Id word, Id offset, Id value) {
|
||||
AtomicBitFieldInsert(word_pointer(ssbo.U32, word, 0), value, bits(offset, count),
|
||||
Const(count));
|
||||
return Id{};
|
||||
};
|
||||
}};
|
||||
const auto atomic{[&](Id (Sirit::Module::*func)(Id, Id, Id, Id, Id)) {
|
||||
return [&, func](Id ssbo, Id word, Id, Id value) {
|
||||
return (this->*func)(U32[1], word_pointer(ssbo, word, 0), scope, zero, value);
|
||||
return [&, func](const StorageDefinitions& ssbo, Id word, Id, Id value) {
|
||||
return (this->*func)(U32[1], word_pointer(ssbo.U32, word, 0), scope, zero, value);
|
||||
};
|
||||
}};
|
||||
const auto cas{[&](Id type, Id helper) {
|
||||
return [&, type, helper](Id ssbo, Id word, Id, Id value) {
|
||||
return OpFunctionCall(type, helper, word, value, ssbo);
|
||||
return [&, type, helper](const StorageDefinitions& ssbo, Id word, Id, Id value) {
|
||||
return OpFunctionCall(type, helper, word, value, ssbo.U32);
|
||||
};
|
||||
}};
|
||||
const auto packed{[&](bool is_half, Id helper) {
|
||||
return [&, is_half, helper](Id ssbo, Id word, Id, Id value) {
|
||||
return [&, is_half, helper](const StorageDefinitions& ssbo, Id word, Id, Id value) {
|
||||
if (is_half) {
|
||||
return OpBitcast(U32[1], OpFunctionCall(F16[2], helper, word, value, ssbo));
|
||||
return OpBitcast(U32[1], OpFunctionCall(F16[2], helper, word, value, ssbo.U32));
|
||||
}
|
||||
return OpPackHalf2x16(U32[1], OpFunctionCall(F32[2], helper, word, value, ssbo));
|
||||
return OpPackHalf2x16(U32[1], OpFunctionCall(F32[2], helper, word, value, ssbo.U32));
|
||||
};
|
||||
}};
|
||||
for (const IR::Block* const block : program.post_order_blocks) {
|
||||
|
||||
@@ -632,6 +632,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
case IR::Opcode::StorageAtomicExchange32:
|
||||
info.used_storage_buffer_types |= IR::Type::U32;
|
||||
break;
|
||||
case IR::Opcode::LoadGlobal64:
|
||||
case IR::Opcode::WriteGlobal64:
|
||||
case IR::Opcode::LoadStorage64:
|
||||
case IR::Opcode::WriteStorage64:
|
||||
case IR::Opcode::StorageAtomicIAdd32x2:
|
||||
@@ -645,6 +647,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
case IR::Opcode::StorageAtomicExchange32x2:
|
||||
info.used_storage_buffer_types |= IR::Type::U32x2;
|
||||
break;
|
||||
case IR::Opcode::LoadGlobal128:
|
||||
case IR::Opcode::WriteGlobal128:
|
||||
case IR::Opcode::LoadStorage128:
|
||||
case IR::Opcode::WriteStorage128:
|
||||
info.used_storage_buffer_types |= IR::Type::U32x4;
|
||||
|
||||
@@ -414,6 +414,14 @@ void FoldSelect(IR::Inst& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void FoldAtomicWrap(IR::Inst& inst, IR::Opcode opcode, u32 addend) {
|
||||
const IR::Value limit{inst.Arg(1)};
|
||||
if (limit.IsImmediate() && limit.U32() == 0xFFFFFFFFU) {
|
||||
inst.ReplaceOpcode(opcode);
|
||||
inst.SetArg(1, IR::Value{addend});
|
||||
}
|
||||
}
|
||||
|
||||
void FoldFPAdd32(IR::Inst& inst) {
|
||||
if (FoldWhenAllImmediates(inst, [](f32 a, f32 b) { return a + b; })) {
|
||||
return;
|
||||
@@ -1104,6 +1112,14 @@ void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
|
||||
IR::Opcode::CompositeInsertF16x4);
|
||||
case IR::Opcode::FSwizzleAdd:
|
||||
return FoldFSwizzleAdd(block, inst);
|
||||
case IR::Opcode::GlobalAtomicInc32:
|
||||
return FoldAtomicWrap(inst, IR::Opcode::GlobalAtomicIAdd32, 1U);
|
||||
case IR::Opcode::GlobalAtomicDec32:
|
||||
return FoldAtomicWrap(inst, IR::Opcode::GlobalAtomicIAdd32, 0xFFFFFFFFU);
|
||||
case IR::Opcode::SharedAtomicInc32:
|
||||
return FoldAtomicWrap(inst, IR::Opcode::SharedAtomicIAdd32, 1U);
|
||||
case IR::Opcode::SharedAtomicDec32:
|
||||
return FoldAtomicWrap(inst, IR::Opcode::SharedAtomicIAdd32, 0xFFFFFFFFU);
|
||||
case IR::Opcode::GetCbufF32:
|
||||
case IR::Opcode::GetCbufU32:
|
||||
if (env.HasHLEMacroState()) {
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/breadth_first_search.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
@@ -49,6 +50,7 @@ using StorageBufferSet =
|
||||
using StorageInstVector = small_vector<StorageInst, 24>;
|
||||
using StorageWritesSet =
|
||||
flat_set<StorageBufferAddr, std::less<StorageBufferAddr>, small_vector<StorageBufferAddr, 16>>;
|
||||
using LocalStores = std::multimap<u32, IR::Value>;
|
||||
|
||||
struct StorageInfo {
|
||||
StorageBufferSet set;
|
||||
@@ -333,7 +335,7 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
|
||||
}
|
||||
|
||||
/// Tries to track the storage buffer address used by a global memory instruction
|
||||
std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) {
|
||||
StorageBufferSet Track(const IR::Value& value, const Bias* bias, const LocalStores& local_stores) {
|
||||
const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> {
|
||||
if (inst->GetOpcode() != IR::Opcode::GetCbufU32 &&
|
||||
inst->GetOpcode() != IR::Opcode::GetCbufU32x2) {
|
||||
@@ -366,11 +368,83 @@ std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias)
|
||||
}
|
||||
return storage_buffer;
|
||||
}};
|
||||
return BreadthFirstSearch(value, pred);
|
||||
StorageBufferSet result;
|
||||
std::unordered_set<const IR::Inst*> visited;
|
||||
small_vector<const IR::Inst*, 32> pending;
|
||||
const auto push{[&](const IR::Value& arg) {
|
||||
if (!arg.IsImmediate() && visited.insert(arg.InstRecursive()).second) {
|
||||
pending.push_back(arg.InstRecursive());
|
||||
}
|
||||
}};
|
||||
push(value);
|
||||
while (!pending.empty()) {
|
||||
const IR::Inst* const inst{pending.back()};
|
||||
pending.pop_back();
|
||||
if (const std::optional<StorageBufferAddr> storage_buffer{pred(inst)}) {
|
||||
result.insert(*storage_buffer);
|
||||
continue;
|
||||
}
|
||||
switch (inst->GetOpcode()) {
|
||||
case IR::Opcode::LoadLocal:
|
||||
if (inst->Arg(0).IsImmediate()) {
|
||||
const auto [begin, end]{local_stores.equal_range(inst->Arg(0).U32())};
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
push(it->second);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case IR::Opcode::SelectU32:
|
||||
case IR::Opcode::SelectU64:
|
||||
push(inst->Arg(1));
|
||||
push(inst->Arg(2));
|
||||
continue;
|
||||
case IR::Opcode::GetCbufU8:
|
||||
case IR::Opcode::GetCbufS8:
|
||||
case IR::Opcode::GetCbufU16:
|
||||
case IR::Opcode::GetCbufS16:
|
||||
case IR::Opcode::GetCbufU32:
|
||||
case IR::Opcode::GetCbufF32:
|
||||
case IR::Opcode::GetCbufU32x2:
|
||||
case IR::Opcode::LoadSharedU8:
|
||||
case IR::Opcode::LoadSharedS8:
|
||||
case IR::Opcode::LoadSharedU16:
|
||||
case IR::Opcode::LoadSharedS16:
|
||||
case IR::Opcode::LoadSharedU32:
|
||||
case IR::Opcode::LoadSharedU64:
|
||||
case IR::Opcode::LoadSharedU128:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (IsGlobalMemory(*inst) || inst->MayHaveSideEffects()) {
|
||||
continue;
|
||||
}
|
||||
for (size_t arg = 0; arg < inst->NumArgs(); ++arg) {
|
||||
push(inst->Arg(arg));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LocalStores GatherLocalStores(const IR::Program& program) {
|
||||
LocalStores stores;
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (const IR::Inst& inst : block->Instructions()) {
|
||||
if (inst.GetOpcode() != IR::Opcode::WriteLocal) {
|
||||
continue;
|
||||
}
|
||||
if (!inst.Arg(0).IsImmediate()) {
|
||||
return {};
|
||||
}
|
||||
stores.emplace(inst.Arg(0).U32(), inst.Arg(1));
|
||||
}
|
||||
}
|
||||
return stores;
|
||||
}
|
||||
|
||||
/// Collects the storage buffer used by a global memory instruction and the instruction itself
|
||||
void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info) {
|
||||
void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info,
|
||||
const LocalStores& local_stores) {
|
||||
// NVN puts storage buffers in a specific range, we have to bias towards these addresses to
|
||||
// avoid getting false positives
|
||||
static constexpr Bias nvn_bias{
|
||||
@@ -387,25 +461,24 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info)
|
||||
}
|
||||
// First try to find storage buffers in the NVN address
|
||||
const IR::U32 low_addr{low_addr_info->value};
|
||||
std::optional<StorageBufferAddr> storage_buffer{Track(low_addr, &nvn_bias)};
|
||||
if (!storage_buffer) {
|
||||
StorageBufferSet candidates{Track(low_addr, &nvn_bias, local_stores)};
|
||||
if (candidates.empty()) {
|
||||
// If it fails, track without a bias
|
||||
storage_buffer = Track(low_addr, nullptr);
|
||||
if (!storage_buffer) {
|
||||
// If that also fails, use NVN fallbacks
|
||||
LOG_WARNING(Shader, "Storage buffer failed to track, using global memory fallbacks");
|
||||
return;
|
||||
}
|
||||
LOG_WARNING(Shader, "Storage buffer tracked without bias, index {} offset {}",
|
||||
storage_buffer->index, storage_buffer->offset);
|
||||
candidates = Track(low_addr, nullptr, local_stores);
|
||||
}
|
||||
if (candidates.size() != 1) {
|
||||
// If that also fails, use NVN fallbacks
|
||||
LOG_WARNING(Shader, "Storage buffer failed to track, using global memory fallbacks");
|
||||
return;
|
||||
}
|
||||
const StorageBufferAddr storage_buffer{*candidates.begin()};
|
||||
// Collect storage buffer and the instruction
|
||||
if (IsGlobalMemoryWrite(inst)) {
|
||||
info.writes.insert(*storage_buffer);
|
||||
info.writes.insert(storage_buffer);
|
||||
}
|
||||
info.set.insert(*storage_buffer);
|
||||
info.set.insert(storage_buffer);
|
||||
info.to_replace.push_back(StorageInst{
|
||||
.storage_buffer{*storage_buffer},
|
||||
.storage_buffer{storage_buffer},
|
||||
.inst = &inst,
|
||||
.block = &block,
|
||||
});
|
||||
@@ -525,12 +598,13 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
|
||||
|
||||
void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) {
|
||||
StorageInfo info;
|
||||
const LocalStores local_stores{GatherLocalStores(program)};
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (!IsGlobalMemory(inst)) {
|
||||
continue;
|
||||
}
|
||||
CollectStorageBuffers(*block, inst, info);
|
||||
CollectStorageBuffers(*block, inst, info, local_stores);
|
||||
}
|
||||
}
|
||||
for (const StorageBufferAddr& storage_buffer : info.set) {
|
||||
|
||||
Reference in New Issue
Block a user