mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-13 13:08:39 +02:00
[vk] properly handle multithreading with global constants without using TLS (#3368)
TL;DR basically multiple threads writing to the same memory, now they dont, everyone happy Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3368 Reviewed-by: DraVee <dravee@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
parent
6afe209b60
commit
ea932fbf40
2 changed files with 102 additions and 94 deletions
|
|
@ -1,17 +1,72 @@
|
||||||
|
// 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 <array>
|
#include <array>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "shader_recompiler/program_header.h"
|
#include "shader_recompiler/program_header.h"
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
#include "shader_recompiler/stage.h"
|
#include "shader_recompiler/stage.h"
|
||||||
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
|
|
||||||
|
namespace Shader::IR {
|
||||||
|
class Inst;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Shader {
|
namespace Shader {
|
||||||
|
|
||||||
|
struct CbufWordKey {
|
||||||
|
u32 index;
|
||||||
|
u32 offset;
|
||||||
|
constexpr bool operator==(const CbufWordKey& o) const noexcept {
|
||||||
|
return index == o.index && offset == o.offset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CbufWordKeyHash {
|
||||||
|
constexpr size_t operator()(const CbufWordKey& k) const noexcept {
|
||||||
|
return (size_t(k.index) << 32) ^ k.offset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HandleKey {
|
||||||
|
u32 index, offset, shift_left;
|
||||||
|
u32 sec_index, sec_offset, sec_shift_left;
|
||||||
|
bool has_secondary;
|
||||||
|
constexpr bool operator==(const HandleKey& o) const noexcept {
|
||||||
|
return std::tie(index, offset, shift_left, sec_index, sec_offset, sec_shift_left, has_secondary)
|
||||||
|
== std::tie(o.index, o.offset, o.shift_left, o.sec_index, o.sec_offset, o.sec_shift_left, o.has_secondary);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct HandleKeyHash {
|
||||||
|
constexpr size_t operator()(const HandleKey& k) const noexcept {
|
||||||
|
size_t h = (size_t(k.index) << 32) ^ k.offset;
|
||||||
|
h ^= (size_t(k.shift_left) << 1);
|
||||||
|
h ^= (size_t(k.sec_index) << 33) ^ (size_t(k.sec_offset) << 2);
|
||||||
|
h ^= (size_t(k.sec_shift_left) << 3);
|
||||||
|
h ^= k.has_secondary ? 0x9e3779b97f4a7c15ULL : 0ULL;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstBufferAddr {
|
||||||
|
u32 index;
|
||||||
|
u32 offset;
|
||||||
|
u32 shift_left;
|
||||||
|
u32 secondary_index;
|
||||||
|
u32 secondary_offset;
|
||||||
|
u32 secondary_shift_left;
|
||||||
|
IR::U32 dynamic_offset;
|
||||||
|
u32 count;
|
||||||
|
bool has_secondary;
|
||||||
|
};
|
||||||
|
|
||||||
class Environment {
|
class Environment {
|
||||||
public:
|
public:
|
||||||
virtual ~Environment() = default;
|
virtual ~Environment() = default;
|
||||||
|
|
@ -69,6 +124,10 @@ protected:
|
||||||
Stage stage{};
|
Stage stage{};
|
||||||
u32 start_address{};
|
u32 start_address{};
|
||||||
bool is_proprietary_driver{};
|
bool is_proprietary_driver{};
|
||||||
|
public:
|
||||||
|
std::unordered_map<CbufWordKey, u32, CbufWordKeyHash> cbuf_word_cache;
|
||||||
|
std::unordered_map<HandleKey, u32, HandleKeyHash> handle_cache;
|
||||||
|
std::unordered_map<const IR::Inst*, ConstBufferAddr> track_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,6 @@
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
namespace {
|
namespace {
|
||||||
struct ConstBufferAddr {
|
|
||||||
u32 index;
|
|
||||||
u32 offset;
|
|
||||||
u32 shift_left;
|
|
||||||
u32 secondary_index;
|
|
||||||
u32 secondary_offset;
|
|
||||||
u32 secondary_shift_left;
|
|
||||||
IR::U32 dynamic_offset;
|
|
||||||
u32 count;
|
|
||||||
bool has_secondary;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureInst {
|
struct TextureInst {
|
||||||
ConstBufferAddr cbuf;
|
ConstBufferAddr cbuf;
|
||||||
IR::Inst* inst;
|
IR::Inst* inst;
|
||||||
|
|
@ -180,97 +168,58 @@ bool IsTextureInstruction(const IR::Inst& inst) {
|
||||||
return IndexedInstruction(inst) != IR::Opcode::Void;
|
return IndexedInstruction(inst) != IR::Opcode::Void;
|
||||||
}
|
}
|
||||||
// Per-pass caches
|
// Per-pass caches
|
||||||
struct CbufWordKey {
|
|
||||||
u32 index;
|
|
||||||
u32 offset;
|
|
||||||
bool operator==(const CbufWordKey& o) const noexcept {
|
|
||||||
return index == o.index && offset == o.offset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct CbufWordKeyHash {
|
|
||||||
size_t operator()(const CbufWordKey& k) const noexcept {
|
|
||||||
return (static_cast<size_t>(k.index) << 32) ^ k.offset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HandleKey {
|
static inline u32 ReadCbufCached(Environment& env, u32 index, u32 offset) {
|
||||||
u32 index, offset, shift_left;
|
const CbufWordKey k{index, offset};
|
||||||
u32 sec_index, sec_offset, sec_shift_left;
|
if (auto it = env.cbuf_word_cache.find(k); it != env.cbuf_word_cache.end()) return it->second;
|
||||||
bool has_secondary;
|
const u32 v = env.ReadCbufValue(index, offset);
|
||||||
bool operator==(const HandleKey& o) const noexcept {
|
env.cbuf_word_cache.emplace(k, v);
|
||||||
return std::tie(index, offset, shift_left,
|
return v;
|
||||||
sec_index, sec_offset, sec_shift_left, has_secondary)
|
}
|
||||||
== std::tie(o.index, o.offset, o.shift_left,
|
|
||||||
o.sec_index, o.sec_offset, o.sec_shift_left, o.has_secondary);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct HandleKeyHash {
|
|
||||||
size_t operator()(const HandleKey& k) const noexcept {
|
|
||||||
size_t h = (static_cast<size_t>(k.index) << 32) ^ k.offset;
|
|
||||||
h ^= (static_cast<size_t>(k.shift_left) << 1);
|
|
||||||
h ^= (static_cast<size_t>(k.sec_index) << 33) ^ (static_cast<size_t>(k.sec_offset) << 2);
|
|
||||||
h ^= (static_cast<size_t>(k.sec_shift_left) << 3);
|
|
||||||
h ^= k.has_secondary ? 0x9e3779b97f4a7c15ULL : 0ULL;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<CbufWordKey, u32, CbufWordKeyHash> g_cbuf_word_cache;
|
static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr& cbuf) {
|
||||||
std::unordered_map<HandleKey, u32, HandleKeyHash> g_handle_cache;
|
const u32 sec_idx = cbuf.has_secondary ? cbuf.secondary_index : cbuf.index;
|
||||||
std::unordered_map<const IR::Inst*, ConstBufferAddr> g_track_cache;
|
const u32 sec_off = cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset;
|
||||||
|
const HandleKey hk{cbuf.index, cbuf.offset, cbuf.shift_left,
|
||||||
|
sec_idx, sec_off, cbuf.secondary_shift_left, cbuf.has_secondary};
|
||||||
|
if (auto it = env.handle_cache.find(hk); it != env.handle_cache.end()) return it->second;
|
||||||
|
|
||||||
static inline u32 ReadCbufCached(Environment& env, u32 index, u32 offset) {
|
const u32 lhs = ReadCbufCached(env, cbuf.index, cbuf.offset) << cbuf.shift_left;
|
||||||
const CbufWordKey k{index, offset};
|
const u32 rhs = ReadCbufCached(env, sec_idx, sec_off) << cbuf.secondary_shift_left;
|
||||||
if (auto it = g_cbuf_word_cache.find(k); it != g_cbuf_word_cache.end()) return it->second;
|
const u32 handle = lhs | rhs;
|
||||||
const u32 v = env.ReadCbufValue(index, offset);
|
env.handle_cache.emplace(hk, handle);
|
||||||
g_cbuf_word_cache.emplace(k, v);
|
return handle;
|
||||||
return v;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr& cbuf) {
|
|
||||||
const u32 sec_idx = cbuf.has_secondary ? cbuf.secondary_index : cbuf.index;
|
|
||||||
const u32 sec_off = cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset;
|
|
||||||
const HandleKey hk{cbuf.index, cbuf.offset, cbuf.shift_left,
|
|
||||||
sec_idx, sec_off, cbuf.secondary_shift_left, cbuf.has_secondary};
|
|
||||||
if (auto it = g_handle_cache.find(hk); it != g_handle_cache.end()) return it->second;
|
|
||||||
|
|
||||||
const u32 lhs = ReadCbufCached(env, cbuf.index, cbuf.offset) << cbuf.shift_left;
|
|
||||||
const u32 rhs = ReadCbufCached(env, sec_idx, sec_off) << cbuf.secondary_shift_left;
|
|
||||||
const u32 handle = lhs | rhs;
|
|
||||||
g_handle_cache.emplace(hk, handle);
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cached variants of existing helpers
|
// Cached variants of existing helpers
|
||||||
static inline TextureType ReadTextureTypeCached(Environment& env, const ConstBufferAddr& cbuf) {
|
static inline TextureType ReadTextureTypeCached(Environment& env, const ConstBufferAddr& cbuf) {
|
||||||
return env.ReadTextureType(GetTextureHandleCached(env, cbuf));
|
return env.ReadTextureType(GetTextureHandleCached(env, cbuf));
|
||||||
}
|
}
|
||||||
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
|
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
|
||||||
const ConstBufferAddr& cbuf) {
|
const ConstBufferAddr& cbuf) {
|
||||||
return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf));
|
return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf));
|
||||||
}
|
}
|
||||||
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||||
const ConstBufferAddr& cbuf) {
|
const ConstBufferAddr& cbuf) {
|
||||||
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
|
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
|
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
|
||||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
|
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
|
||||||
if (const IR::Inst* key = v.InstRecursive()) {
|
if (const IR::Inst* key = v.InstRecursive()) {
|
||||||
if (auto it = g_track_cache.find(key); it != g_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);
|
auto found = Track(v, env);
|
||||||
if (found) g_track_cache.emplace(key, *found);
|
if (found) env.track_cache.emplace(key, *found);
|
||||||
return found;
|
return found;
|
||||||
}
|
|
||||||
return Track(v, env);
|
|
||||||
}
|
}
|
||||||
|
return Track(v, env);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
|
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
|
||||||
|
|
||||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
|
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
|
||||||
return IR::BreadthFirstSearch(
|
return IR::BreadthFirstSearch(value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
|
||||||
value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||||
|
|
@ -617,9 +566,9 @@ void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_
|
||||||
|
|
||||||
void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {
|
void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {
|
||||||
// reset per-pass caches
|
// reset per-pass caches
|
||||||
g_cbuf_word_cache.clear();
|
env.cbuf_word_cache.clear();
|
||||||
g_handle_cache.clear();
|
env.handle_cache.clear();
|
||||||
g_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) {
|
||||||
for (IR::Inst& inst : block->Instructions()) {
|
for (IR::Inst& inst : block->Instructions()) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue