mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
Compare commits
6 commits
d361dfcaf0
...
10391da0d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10391da0d3 | ||
|
|
ab7f9b3f16 | ||
|
|
7c44c8fe4b | ||
|
|
23fd83e3d5 | ||
|
|
fed5cfac53 | ||
|
|
de7c40d330 |
|
|
@ -226,6 +226,7 @@ struct PooledMemoryResource {
|
|||
{.pmemAddress = block.beginAddress(), .type = type},
|
||||
false);
|
||||
freeBlocks.pop_back();
|
||||
used += mapVirtualRange.size();
|
||||
|
||||
virtualRange = rx::AddressRange::fromBeginEnd(
|
||||
mapVirtualRange.endAddress(), virtualRange.endAddress());
|
||||
|
|
|
|||
|
|
@ -453,20 +453,16 @@ Cache::ShaderResources::eval(ir::InstructionId instId,
|
|||
case 8:
|
||||
result = readPointer<std::uint64_t>(address);
|
||||
break;
|
||||
case 12:
|
||||
result = readPointer<u32vec3>(address);
|
||||
break;
|
||||
case 16:
|
||||
result = readPointer<u32vec4>(address);
|
||||
break;
|
||||
case 32:
|
||||
result = readPointer<std::array<std::uint32_t, 8>>(address);
|
||||
break;
|
||||
case 64:
|
||||
result = readPointer<std::array<std::uint32_t, 16>>(address);
|
||||
break;
|
||||
|
||||
default:
|
||||
rx::die("unexpected pointer load size {}", loadSize);
|
||||
rx::dieIf(loadSize % sizeof(std::uint32_t), "unaligned load size {}",
|
||||
loadSize);
|
||||
|
||||
for (std::int32_t offset = 0; offset < loadSize;
|
||||
offset += sizeof(std::uint32_t)) {
|
||||
result.add(readPointer<std::uint32_t>(address + offset));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -2368,7 +2364,7 @@ Cache::Shader Cache::GraphicsTag::getShader(
|
|||
std::bit_cast<std::uint32_t>(context.paClVports[slot.data].zScale);
|
||||
break;
|
||||
case gcn::ConfigType::PsInputVGpr:
|
||||
if (slot.data > psVgprInput.size()) {
|
||||
if (slot.data >= psVgprInput.size()) {
|
||||
configPtr[index] = ~0;
|
||||
} else {
|
||||
configPtr[index] = std::bit_cast<std::uint32_t>(psVgprInput[slot.data]);
|
||||
|
|
@ -2456,9 +2452,9 @@ Cache::ComputeTag::getShader(const Registers::ComputeConfig &pgm) {
|
|||
gcn::Environment env{
|
||||
.vgprCount = pgm.rsrc1.getVGprCount(),
|
||||
.sgprCount = pgm.rsrc1.getSGprCount(),
|
||||
.numThreadX = static_cast<std::uint8_t>(pgm.numThreadX),
|
||||
.numThreadY = static_cast<std::uint8_t>(pgm.numThreadY),
|
||||
.numThreadZ = static_cast<std::uint8_t>(pgm.numThreadZ),
|
||||
.numThreadX = std::max<std::uint8_t>(pgm.numThreadX, 1),
|
||||
.numThreadY = std::max<std::uint8_t>(pgm.numThreadY, 1),
|
||||
.numThreadZ = std::max<std::uint8_t>(pgm.numThreadZ, 1),
|
||||
.userSgprs = std::span(pgm.userData.data(), pgm.rsrc2.userSgpr),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ void Device::start() {
|
|||
});
|
||||
}
|
||||
|
||||
std::jthread vblankThread([](const std::stop_token &stopToken) {
|
||||
std::jthread vblankThread([this](const std::stop_token &stopToken) {
|
||||
orbis::g_context->deviceEventEmitter->emit(
|
||||
orbis::kEvFiltDisplay,
|
||||
[=](orbis::KNote *note) -> std::optional<orbis::intptr_t> {
|
||||
|
|
@ -330,6 +330,8 @@ void Device::start() {
|
|||
std::chrono::duration_cast<std::chrono::nanoseconds>(period);
|
||||
std::this_thread::sleep_until(prevVBlank);
|
||||
|
||||
vblankCount++;
|
||||
|
||||
orbis::g_context->deviceEventEmitter->emit(
|
||||
orbis::kEvFiltDisplay,
|
||||
[=](orbis::KNote *note) -> std::optional<orbis::intptr_t> {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ struct DeviceContext {
|
|||
static constexpr auto kMaxProcessCount = 6;
|
||||
|
||||
PadState kbPadState{};
|
||||
std::atomic<std::uint64_t> vblankCount{};
|
||||
std::atomic<std::uint64_t> cpuCacheCommands[kMaxProcessCount][4]{};
|
||||
rx::shared_atomic32 cpuCacheCommandsIdle[kMaxProcessCount]{};
|
||||
rx::shared_atomic32 gpuCacheCommand[kMaxProcessCount]{};
|
||||
|
|
|
|||
|
|
@ -2,33 +2,156 @@
|
|||
|
||||
#include "Vector.hpp"
|
||||
#include "ir/Value.hpp"
|
||||
#include "rx/align.hpp"
|
||||
#include "rx/die.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
#include <cstring>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace shader::eval {
|
||||
struct Value {
|
||||
using Storage = std::variant<
|
||||
std::nullptr_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t,
|
||||
std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, float16_t,
|
||||
float32_t, float64_t, u8vec2, u8vec3, u8vec4, i8vec2, i8vec3, i8vec4,
|
||||
u16vec2, u16vec3, u16vec4, i16vec2, i16vec3, i16vec4, u32vec2, u32vec3,
|
||||
u32vec4, i32vec2, i32vec3, i32vec4, u64vec2, u64vec3, u64vec4, i64vec2,
|
||||
i64vec3, i64vec4, f32vec2, f32vec3, f32vec4, f64vec2, f64vec3, f64vec4,
|
||||
f16vec2, f16vec3, f16vec4, bool, bvec2, bvec3, bvec4,
|
||||
std::array<uint32_t, 8>, std::array<std::uint32_t, 16>>;
|
||||
static constexpr auto StorageSize = std::variant_size_v<Storage>;
|
||||
Storage storage;
|
||||
using Types = std::tuple<std::nullptr_t, bool, int8_t, int16_t, int32_t,
|
||||
int64_t, uint8_t, uint16_t, uint32_t, uint64_t,
|
||||
float16_t, float32_t, float64_t>;
|
||||
|
||||
static constexpr auto kMaxElementCount = 32;
|
||||
static constexpr auto kMaxTypeAlignment = [] {
|
||||
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
std::size_t result = 1;
|
||||
((result = std::max(alignof(std::tuple_element_t<I, Types>), result)),
|
||||
...);
|
||||
return result;
|
||||
};
|
||||
return impl(std::make_index_sequence<std::tuple_size_v<Types>>{});
|
||||
}();
|
||||
static constexpr auto kMaxTypeSize = [] {
|
||||
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
std::size_t result = 1;
|
||||
((result = std::max(sizeof(std::tuple_element_t<I, Types>), result)),
|
||||
...);
|
||||
return result;
|
||||
};
|
||||
return rx::alignUp(
|
||||
impl(std::make_index_sequence<std::tuple_size_v<Types>>{}),
|
||||
kMaxTypeAlignment);
|
||||
}();
|
||||
|
||||
template <typename T> static consteval std::size_t getTypeIndex() {
|
||||
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
std::size_t result = -1;
|
||||
((result =
|
||||
std::is_same_v<T, std::tuple_element_t<I, Types>> ? I : result),
|
||||
...);
|
||||
return result;
|
||||
};
|
||||
return impl(std::make_index_sequence<std::tuple_size_v<Types>>{});
|
||||
}
|
||||
|
||||
static constexpr auto StorageSize = std::tuple_size_v<Types>;
|
||||
|
||||
template <typename T, typename RT = std::invoke_result_t<
|
||||
T, std::span<std::tuple_element_t<0, Types>>>>
|
||||
RT visit(T &&cb) const {
|
||||
static constexpr auto table = [] {
|
||||
std::array<RT (*)(void *cb, const char *data, std::uint32_t count),
|
||||
std::tuple_size_v<Types>>
|
||||
result;
|
||||
|
||||
auto impl = [&]<std::size_t... I>(std::index_sequence<I...>) {
|
||||
((result[I] = [](void *cb, const char *data,
|
||||
std::uint32_t count) -> RT {
|
||||
return (*reinterpret_cast<T *>(cb))(std::span(
|
||||
reinterpret_cast<const std::tuple_element_t<I, Types> *>(data),
|
||||
count));
|
||||
}),
|
||||
...);
|
||||
};
|
||||
|
||||
impl(std::make_index_sequence<std::tuple_size_v<Types>>{});
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
return table[mTypeIndex](&cb, mData, mCount);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t getConstituentSize() const {
|
||||
return visit([](auto values) -> std::size_t {
|
||||
if constexpr (std::is_same_v<decltype(values[0]), std::nullptr_t>) {
|
||||
return 0;
|
||||
} else {
|
||||
return sizeof(values[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
explicit operator bool() const { return !empty(); }
|
||||
bool empty() const { return storage.index() == 0; }
|
||||
[[nodiscard]] bool empty() const {
|
||||
return mCount == 0 || mTypeIndex == getTypeIndex<std::nullptr_t>();
|
||||
}
|
||||
[[nodiscard]] std::size_t size() const { return mCount; }
|
||||
|
||||
Value() : storage(nullptr) {}
|
||||
Value() = default;
|
||||
|
||||
template <typename FT, typename... T>
|
||||
requires(getTypeIndex<FT>() < std::tuple_size_v<Types> &&
|
||||
(std::is_same_v<FT, T> && ...))
|
||||
Value(FT firstValue, T... value) {
|
||||
add(firstValue);
|
||||
(add(value), ...);
|
||||
}
|
||||
|
||||
void add(const Value &value) {
|
||||
if (value.mCount != 1) {
|
||||
mTypeIndex = getTypeIndex<std::nullptr_t>();
|
||||
mCount = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCount == 0) {
|
||||
mTypeIndex = value.mTypeIndex;
|
||||
mCount = 1;
|
||||
std::memcpy(mData, value.mData, kMaxTypeSize);
|
||||
return;
|
||||
}
|
||||
|
||||
rx::dieIf(mCount >= kMaxElementCount, "storage too small");
|
||||
|
||||
if (mTypeIndex != value.mTypeIndex) {
|
||||
mTypeIndex = getTypeIndex<std::nullptr_t>();
|
||||
mCount = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
auto index = mCount++;
|
||||
auto elemSize = getConstituentSize();
|
||||
std::memcpy(mData + elemSize * index, value.mData, kMaxTypeSize);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value(T &&value)
|
||||
requires requires { Storage(std::forward<T>(value)); }
|
||||
: storage(std::forward<T>(value)) {}
|
||||
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
|
||||
void add(T value) {
|
||||
if (mCount == 0) {
|
||||
mTypeIndex = getTypeIndex<T>();
|
||||
mCount = 1;
|
||||
*getData<T>() = value;
|
||||
return;
|
||||
}
|
||||
|
||||
rx::dieIf(mCount >= kMaxElementCount, "storage too small");
|
||||
|
||||
if (mTypeIndex != getTypeIndex<T>()) {
|
||||
mTypeIndex = getTypeIndex<std::nullptr_t>();
|
||||
mCount = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
getData<T>()[mCount++] = value;
|
||||
}
|
||||
|
||||
static Value compositeConstruct(ir::Value type,
|
||||
std::span<const Value> constituents);
|
||||
|
|
@ -52,21 +175,43 @@ struct Value {
|
|||
std::optional<std::int64_t> sExtScalar() const;
|
||||
|
||||
template <typename T>
|
||||
requires requires { std::get<T>(storage); }
|
||||
T get() const {
|
||||
return std::get<T>(storage);
|
||||
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
|
||||
[[nodiscard]] const T &get(std::size_t index = 0) const {
|
||||
rx::dieIf(mTypeIndex != getTypeIndex<T>(),
|
||||
"eval::Value::get(): invalid type");
|
||||
rx::dieIf(index >= std::tuple_size_v<Types>,
|
||||
"eval::Value::get(): invalid index");
|
||||
return getData<T>()[index];
|
||||
}
|
||||
|
||||
template <auto I>
|
||||
requires(I < std::tuple_size_v<Types>)
|
||||
[[nodiscard]] const std::tuple_element_t<I, Types> &
|
||||
get(std::size_t index = 0) const {
|
||||
rx::dieIf(mTypeIndex != I, "eval::Value::get(): invalid type");
|
||||
rx::dieIf(index >= std::tuple_size_v<Types>,
|
||||
"eval::Value::get(): invalid index");
|
||||
return getData<std::tuple_element_t<I, Types>>()[index];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires requires { std::get<T>(storage); }
|
||||
std::optional<T> as() const {
|
||||
if (auto result = std::get_if<T>(&storage)) {
|
||||
return *result;
|
||||
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
|
||||
[[nodiscard]] std::optional<T> as(std::size_t index = 0) const {
|
||||
rx::dieIf(index >= std::tuple_size_v<Types>,
|
||||
"eval::Value::as(): invalid index");
|
||||
if (mTypeIndex == getTypeIndex<T>()) {
|
||||
return getData<T>()[index];
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(getTypeIndex<T>() < std::tuple_size_v<Types>)
|
||||
[[nodiscard]] bool is() const {
|
||||
return mTypeIndex == getTypeIndex<T>();
|
||||
}
|
||||
|
||||
Value operator+(const Value &rhs) const;
|
||||
Value operator-(const Value &rhs) const;
|
||||
Value operator*(const Value &rhs) const;
|
||||
|
|
@ -89,5 +234,17 @@ struct Value {
|
|||
Value operator-() const;
|
||||
Value operator~() const;
|
||||
Value operator!() const;
|
||||
|
||||
std::size_t index() const { return mTypeIndex; }
|
||||
|
||||
private:
|
||||
template <typename T> T *getData() { return reinterpret_cast<T *>(mData); }
|
||||
template <typename T> const T *getData() const {
|
||||
return reinterpret_cast<const T *>(mData);
|
||||
}
|
||||
|
||||
std::uint32_t mTypeIndex = 0;
|
||||
std::uint32_t mCount = 0;
|
||||
alignas(kMaxTypeAlignment) char mData[kMaxTypeSize * kMaxElementCount];
|
||||
};
|
||||
} // namespace shader::eval
|
||||
|
|
|
|||
|
|
@ -2,132 +2,15 @@
|
|||
#include "dialect.hpp"
|
||||
#include "ir.hpp"
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace shader;
|
||||
|
||||
template <typename Cond, typename... Args> consteval bool testVisitCond() {
|
||||
if constexpr (std::is_same_v<Cond, void>) {
|
||||
return true;
|
||||
} else {
|
||||
return Cond{}(std::remove_cvref_t<Args>{}...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Cond, std::size_t U> consteval bool testVisitCond() {
|
||||
if constexpr (U >= eval::Value::StorageSize) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<Cond, void>) {
|
||||
return true;
|
||||
} else {
|
||||
return Cond{}(std::variant_alternative_t<U, eval::Value::Storage>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Cond = void, size_t I = 0>
|
||||
constexpr eval::Value visitImpl(const eval::Value &variant, auto &&fn) {
|
||||
|
||||
#define DEFINE_CASE(N) \
|
||||
case I + N: \
|
||||
if constexpr (testVisitCond<Cond, I + N>()) { \
|
||||
return std::forward<decltype(fn)>(fn)(std::get<I + N>(variant.storage)); \
|
||||
} else { \
|
||||
return {}; \
|
||||
}
|
||||
|
||||
switch (variant.storage.index()) {
|
||||
DEFINE_CASE(0);
|
||||
DEFINE_CASE(1);
|
||||
DEFINE_CASE(2);
|
||||
DEFINE_CASE(3);
|
||||
DEFINE_CASE(4);
|
||||
DEFINE_CASE(5);
|
||||
DEFINE_CASE(6);
|
||||
DEFINE_CASE(7);
|
||||
DEFINE_CASE(8);
|
||||
DEFINE_CASE(9);
|
||||
DEFINE_CASE(10);
|
||||
DEFINE_CASE(11);
|
||||
DEFINE_CASE(12);
|
||||
DEFINE_CASE(13);
|
||||
DEFINE_CASE(14);
|
||||
DEFINE_CASE(15);
|
||||
DEFINE_CASE(16);
|
||||
DEFINE_CASE(17);
|
||||
DEFINE_CASE(18);
|
||||
DEFINE_CASE(19);
|
||||
DEFINE_CASE(20);
|
||||
DEFINE_CASE(21);
|
||||
DEFINE_CASE(22);
|
||||
DEFINE_CASE(23);
|
||||
DEFINE_CASE(24);
|
||||
DEFINE_CASE(25);
|
||||
DEFINE_CASE(26);
|
||||
DEFINE_CASE(27);
|
||||
DEFINE_CASE(28);
|
||||
DEFINE_CASE(29);
|
||||
DEFINE_CASE(30);
|
||||
DEFINE_CASE(31);
|
||||
DEFINE_CASE(32);
|
||||
DEFINE_CASE(33);
|
||||
DEFINE_CASE(34);
|
||||
DEFINE_CASE(35);
|
||||
DEFINE_CASE(36);
|
||||
DEFINE_CASE(37);
|
||||
DEFINE_CASE(38);
|
||||
DEFINE_CASE(39);
|
||||
DEFINE_CASE(40);
|
||||
DEFINE_CASE(41);
|
||||
DEFINE_CASE(42);
|
||||
DEFINE_CASE(43);
|
||||
DEFINE_CASE(44);
|
||||
DEFINE_CASE(45);
|
||||
DEFINE_CASE(46);
|
||||
DEFINE_CASE(47);
|
||||
DEFINE_CASE(48);
|
||||
DEFINE_CASE(49);
|
||||
DEFINE_CASE(50);
|
||||
DEFINE_CASE(51);
|
||||
DEFINE_CASE(52);
|
||||
DEFINE_CASE(53);
|
||||
DEFINE_CASE(54);
|
||||
DEFINE_CASE(55);
|
||||
DEFINE_CASE(56);
|
||||
DEFINE_CASE(57);
|
||||
DEFINE_CASE(58);
|
||||
DEFINE_CASE(59);
|
||||
DEFINE_CASE(60);
|
||||
DEFINE_CASE(61);
|
||||
DEFINE_CASE(62);
|
||||
DEFINE_CASE(63);
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
|
||||
constexpr auto NextIndex = I + 64;
|
||||
|
||||
if constexpr (NextIndex < eval::Value::StorageSize) {
|
||||
return visitImpl<Cond, NextIndex>(std::forward<decltype(fn)>(fn),
|
||||
std::forward<decltype(variant)>(variant));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename Cond = void, typename Cb>
|
||||
constexpr eval::Value visitScalarType(ir::Value type, Cb &&cb)
|
||||
requires requires {
|
||||
{ std::forward<Cb>(cb)(int{}) } -> std::same_as<eval::Value>;
|
||||
}
|
||||
{
|
||||
auto invoke = [&](auto type) -> eval::Value {
|
||||
if constexpr (testVisitCond<Cond, std::remove_cvref_t<decltype(type)>>()) {
|
||||
return std::forward<Cb>(cb)(type);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool invokeWithType(ir::Value type, T &&invoke) {
|
||||
if (type == ir::spv::OpTypeBool) {
|
||||
return invoke(bool{});
|
||||
invoke(bool{});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == ir::spv::OpTypeInt) {
|
||||
|
|
@ -136,213 +19,106 @@ constexpr eval::Value visitScalarType(ir::Value type, Cb &&cb)
|
|||
switch (*type.getOperand(0).getAsInt32()) {
|
||||
case 8:
|
||||
if (isSigned) {
|
||||
return invoke(std::int8_t{});
|
||||
invoke(std::int8_t{});
|
||||
return true;
|
||||
}
|
||||
return invoke(std::uint8_t{});
|
||||
invoke(std::uint8_t{});
|
||||
return true;
|
||||
|
||||
case 16:
|
||||
if (isSigned) {
|
||||
return invoke(std::int16_t{});
|
||||
invoke(std::int16_t{});
|
||||
return true;
|
||||
}
|
||||
return invoke(std::uint16_t{});
|
||||
invoke(std::uint16_t{});
|
||||
return true;
|
||||
|
||||
case 32:
|
||||
if (isSigned) {
|
||||
return invoke(std::int32_t{});
|
||||
invoke(std::int32_t{});
|
||||
return true;
|
||||
}
|
||||
return invoke(std::uint32_t{});
|
||||
invoke(std::uint32_t{});
|
||||
return true;
|
||||
|
||||
case 64:
|
||||
if (isSigned) {
|
||||
return invoke(std::int64_t{});
|
||||
invoke(std::int64_t{});
|
||||
return true;
|
||||
}
|
||||
return invoke(std::uint64_t{});
|
||||
invoke(std::uint64_t{});
|
||||
return true;
|
||||
}
|
||||
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == ir::spv::OpTypeFloat) {
|
||||
switch (*type.getOperand(0).getAsInt32()) {
|
||||
case 16:
|
||||
return invoke(shader::float16_t{});
|
||||
invoke(shader::float16_t{});
|
||||
return true;
|
||||
|
||||
case 32:
|
||||
return invoke(shader::float32_t{});
|
||||
invoke(shader::float32_t{});
|
||||
return true;
|
||||
|
||||
case 64:
|
||||
return invoke(shader::float64_t{});
|
||||
invoke(shader::float64_t{});
|
||||
return true;
|
||||
}
|
||||
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename Cond = void, typename Cb>
|
||||
constexpr eval::Value visitType(ir::Value type, Cb &&cb)
|
||||
requires requires {
|
||||
{ std::forward<Cb>(cb)(int{}) } -> std::same_as<eval::Value>;
|
||||
}
|
||||
{
|
||||
if (type == ir::spv::OpTypeInt || type == ir::spv::OpTypeFloat ||
|
||||
type == ir::spv::OpTypeBool) {
|
||||
return visitScalarType<Cond>(type, cb);
|
||||
}
|
||||
|
||||
auto invoke = [&](auto type) -> eval::Value {
|
||||
if constexpr (testVisitCond<Cond, std::remove_cvref_t<decltype(type)>>()) {
|
||||
return std::forward<Cb>(cb)(type);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
if (type == ir::spv::OpTypeVector) {
|
||||
switch (*type.getOperand(1).getAsInt32()) {
|
||||
case 2:
|
||||
return visitScalarType(
|
||||
type.getOperand(0).getAsValue(),
|
||||
[&]<typename T>(T) { return invoke(shader::Vector<T, 2>{}); });
|
||||
|
||||
case 3:
|
||||
return visitScalarType(
|
||||
type.getOperand(0).getAsValue(),
|
||||
[&]<typename T>(T) { return invoke(shader::Vector<T, 3>{}); });
|
||||
|
||||
case 4:
|
||||
return visitScalarType(
|
||||
type.getOperand(0).getAsValue(),
|
||||
[&]<typename T>(T) { return invoke(shader::Vector<T, 4>{}); });
|
||||
}
|
||||
|
||||
return {};
|
||||
return invokeWithType(type.getOperand(0).getAsValue(),
|
||||
std::forward<T>(invoke));
|
||||
}
|
||||
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Cond = void, typename Cb>
|
||||
eval::Value visit(const eval::Value &value, Cb &&cb) {
|
||||
using VisitCond = decltype([](auto &&storage) {
|
||||
using T = std::remove_cvref_t<decltype(storage)>;
|
||||
if constexpr (std::is_same_v<T, std::nullptr_t>) {
|
||||
return false;
|
||||
} else {
|
||||
return testVisitCond<Cond, T>();
|
||||
}
|
||||
static constexpr std::size_t getIrTypeIndex(ir::Value type) {
|
||||
std::size_t result = 0;
|
||||
|
||||
invokeWithType(type, [&](auto type) {
|
||||
result = eval::Value::getTypeIndex<decltype(type)>();
|
||||
});
|
||||
|
||||
return visitImpl<VisitCond>(value, std::forward<Cb>(cb));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Cb>
|
||||
eval::Value visit2(auto &&cond, const eval::Value &value, Cb &&cb) {
|
||||
if constexpr (cond()) {
|
||||
return visitImpl(value, std::forward<Cb>(cb));
|
||||
} else {
|
||||
return {};
|
||||
static constexpr std::size_t getIrTypeConstituents(ir::Value type) {
|
||||
if (type == ir::spv::OpTypeVector) {
|
||||
return *type.getOperand(1).getAsInt32();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename ValueCond = void, typename TypeVisitCond = void,
|
||||
typename TypeValueVisitCond = void, typename Cb>
|
||||
eval::Value visitWithType(const eval::Value &value, ir::Value type, Cb &&cb) {
|
||||
using ValueVisitCond = decltype([](auto storage) {
|
||||
if constexpr (std::is_same_v<decltype(storage), std::nullptr_t>) {
|
||||
return false;
|
||||
} else {
|
||||
return testVisitCond<ValueCond, decltype(storage)>();
|
||||
}
|
||||
});
|
||||
|
||||
return visitImpl<ValueVisitCond>(value, [&](auto &&value) -> eval::Value {
|
||||
return visitType<TypeVisitCond>(type, [&](auto type) -> eval::Value {
|
||||
if constexpr (testVisitCond<TypeValueVisitCond, decltype(type),
|
||||
decltype(value)>()) {
|
||||
return std::forward<Cb>(cb)(type, value);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T> struct ComponentTypeImpl {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N> struct ComponentTypeImpl<Vector<T, N>> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct ComponentTypeImpl<std::array<T, N>> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T> struct MakeSignedImpl {
|
||||
using type = std::make_signed_t<T>;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N> struct MakeSignedImpl<Vector<T, N>> {
|
||||
using type = Vector<std::make_signed_t<T>, N>;
|
||||
};
|
||||
template <typename T> struct MakeUnsignedImpl {
|
||||
using type = std::make_unsigned_t<T>;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N> struct MakeUnsignedImpl<Vector<T, N>> {
|
||||
using type = Vector<std::make_unsigned_t<T>, N>;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <typename T> using ComponentType = typename ComponentTypeImpl<T>::type;
|
||||
template <typename T> using MakeSigned = typename MakeSignedImpl<T>::type;
|
||||
template <typename T> using MakeUnsigned = typename MakeUnsignedImpl<T>::type;
|
||||
|
||||
template <typename> constexpr std::size_t Components = 1;
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::size_t Components<Vector<T, N>> = N;
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::size_t Components<std::array<T, N>> = N;
|
||||
|
||||
template <typename> constexpr bool IsArray = false;
|
||||
template <typename T, std::size_t N>
|
||||
constexpr bool IsArray<std::array<T, N>> = true;
|
||||
|
||||
eval::Value
|
||||
eval::Value::compositeConstruct(ir::Value type,
|
||||
std::span<const eval::Value> constituents) {
|
||||
using Cond =
|
||||
decltype([](auto type) { return Components<decltype(type)> > 1; });
|
||||
if (getIrTypeConstituents(type) != constituents.size()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return visitType<Cond>(type, [&](auto type) -> Value {
|
||||
constexpr std::size_t N = Components<decltype(type)>;
|
||||
if (N != constituents.size()) {
|
||||
return {};
|
||||
}
|
||||
auto typeIndex = getIrTypeIndex(type);
|
||||
|
||||
decltype(type) result;
|
||||
eval::Value result;
|
||||
for (auto &elem : constituents) {
|
||||
result.add(elem);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (auto value = constituents[i].as<ComponentType<decltype(type)>>()) {
|
||||
result[i] = *value;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (result.index() != typeIndex) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
eval::Value eval::Value::compositeExtract(const Value &index) const {
|
||||
using Cond =
|
||||
decltype([](auto type) { return Components<decltype(type)> > 1; });
|
||||
|
||||
auto optIndexInt = index.zExtScalar();
|
||||
if (!optIndexInt) {
|
||||
return {};
|
||||
|
|
@ -350,321 +126,249 @@ eval::Value eval::Value::compositeExtract(const Value &index) const {
|
|||
|
||||
auto indexInt = *optIndexInt;
|
||||
|
||||
return visit<Cond>(*this, [&](auto &&value) -> Value {
|
||||
using ValueType = std::remove_cvref_t<decltype(value)>;
|
||||
constexpr std::size_t N = Components<ValueType>;
|
||||
if (indexInt >= size()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (indexInt >= N) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return value[indexInt];
|
||||
});
|
||||
eval::Value result;
|
||||
result.mTypeIndex = mTypeIndex;
|
||||
result.mCount = 1;
|
||||
std::memcpy(result.mData, mData + indexInt * getConstituentSize(),
|
||||
kMaxTypeSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
eval::Value eval::Value::isNan() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
|
||||
!IsArray<decltype(type)>;
|
||||
});
|
||||
return visit([](auto value) {
|
||||
eval::Value result;
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) -> Value {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
|
||||
if constexpr (N == 1) {
|
||||
return std::isnan(value);
|
||||
} else {
|
||||
Vector<bool, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
result[i] = std::isnan(value[i]);
|
||||
if constexpr (std::is_floating_point_v<
|
||||
typename decltype(value)::value_type>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
result.add(std::isnan(value[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
eval::Value eval::Value::isInf() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
|
||||
!IsArray<decltype(type)>;
|
||||
});
|
||||
return visit([](auto value) {
|
||||
eval::Value result;
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) -> Value {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
|
||||
if constexpr (N == 1) {
|
||||
return std::isinf(value);
|
||||
} else {
|
||||
Vector<bool, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
result[i] = std::isinf(value[i]);
|
||||
if constexpr (std::is_floating_point_v<
|
||||
typename decltype(value)::value_type>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
result.add(std::isinf(value[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
eval::Value eval::Value::isFinite() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_floating_point_v<ComponentType<decltype(type)>>;
|
||||
});
|
||||
return visit([](auto value) {
|
||||
eval::Value result;
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) -> Value {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
|
||||
if constexpr (N == 1) {
|
||||
return std::isfinite(value);
|
||||
} else {
|
||||
Vector<bool, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
result[i] = std::isfinite(value[i]);
|
||||
if constexpr (std::is_floating_point_v<
|
||||
typename decltype(value)::value_type>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
result.add(std::isfinite(value[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
eval::Value eval::Value::makeUnsigned() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_integral_v<ComponentType<decltype(type)>> &&
|
||||
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
!IsArray<decltype(type)>;
|
||||
});
|
||||
return visit([](auto value) {
|
||||
eval::Value result;
|
||||
using value_type = typename decltype(value)::value_type;
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) -> Value {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
using T = std::make_unsigned_t<
|
||||
ComponentType<std::remove_cvref_t<decltype(value)>>>;
|
||||
|
||||
if constexpr (N == 1) {
|
||||
return static_cast<T>(value);
|
||||
} else {
|
||||
Vector<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
result[i] = static_cast<T>(value[i]);
|
||||
if constexpr (std::is_integral_v<value_type> &&
|
||||
!std::is_same_v<value_type, bool>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
result.add(static_cast<std::make_unsigned_t<value_type>>(value[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
eval::Value eval::Value::makeSigned() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_integral_v<ComponentType<decltype(type)>> &&
|
||||
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
!IsArray<decltype(type)>;
|
||||
});
|
||||
return visit([](auto value) {
|
||||
eval::Value result;
|
||||
using value_type = typename decltype(value)::value_type;
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) -> Value {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
using T =
|
||||
std::make_signed_t<ComponentType<std::remove_cvref_t<decltype(value)>>>;
|
||||
|
||||
if constexpr (N == 1) {
|
||||
return static_cast<T>(value);
|
||||
} else {
|
||||
Vector<T, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
result[i] = static_cast<T>(value[i]);
|
||||
if constexpr (std::is_integral_v<value_type> &&
|
||||
!std::is_same_v<value_type, bool>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
result.add(static_cast<std::make_signed_t<value_type>>(value[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
eval::Value eval::Value::all() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
(Components<decltype(type)> > 1) && !IsArray<decltype(type)>;
|
||||
});
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (!value[i]) {
|
||||
return false;
|
||||
return visit([](auto value) {
|
||||
if constexpr (std::is_same_v<typename decltype(value)::value_type, bool>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
if (!value[i]) {
|
||||
return eval::Value(false);
|
||||
}
|
||||
}
|
||||
return eval::Value(true);
|
||||
}
|
||||
return true;
|
||||
|
||||
return eval::Value();
|
||||
});
|
||||
}
|
||||
|
||||
eval::Value eval::Value::any() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
(Components<decltype(type)> > 1) && !IsArray<decltype(type)>;
|
||||
});
|
||||
|
||||
return visit<Cond>(*this, [](auto &&value) {
|
||||
constexpr std::size_t N = Components<std::remove_cvref_t<decltype(value)>>;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (value[i]) {
|
||||
return true;
|
||||
return visit([](auto value) {
|
||||
if constexpr (std::is_same_v<typename decltype(value)::value_type, bool>) {
|
||||
for (std::size_t i = 0; i < value.size(); ++i) {
|
||||
if (value[i]) {
|
||||
return eval::Value(true);
|
||||
}
|
||||
}
|
||||
return eval::Value(false);
|
||||
}
|
||||
return false;
|
||||
|
||||
return eval::Value();
|
||||
});
|
||||
}
|
||||
|
||||
eval::Value eval::Value::select(const Value &trueValue,
|
||||
const Value &falseValue) const {
|
||||
using Cond = decltype([](auto type) consteval {
|
||||
return std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
!IsArray<decltype(type)>;
|
||||
});
|
||||
auto optCond = as<bool>();
|
||||
if (!optCond) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return visit<Cond>(*this, [&](auto &&cond) -> Value {
|
||||
using CondType = std::remove_cvref_t<decltype(cond)>;
|
||||
using TrueCond = decltype([](auto type) consteval {
|
||||
return Components<decltype(type)> == Components<CondType>;
|
||||
});
|
||||
|
||||
return visit<TrueCond>(trueValue, [&](auto &&trueValue) {
|
||||
using TrueValue = std::remove_cvref_t<decltype(trueValue)>;
|
||||
using FalseCond = decltype([](auto type) {
|
||||
return std::is_same_v<TrueValue, std::remove_cvref_t<decltype(type)>>;
|
||||
});
|
||||
|
||||
return visit(falseValue, [&](auto &&falseValue) -> Value {
|
||||
if constexpr (std::is_same_v<TrueValue, std::remove_cvref_t<
|
||||
decltype(falseValue)>>) {
|
||||
constexpr std::size_t N = Components<CondType>;
|
||||
|
||||
if constexpr (N == 1) {
|
||||
return cond ? trueValue : falseValue;
|
||||
} else {
|
||||
Vector<bool, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
result[i] = cond[i] ? trueValue[i] : falseValue[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
auto cond = *optCond;
|
||||
return cond ? trueValue : falseValue;
|
||||
}
|
||||
|
||||
eval::Value eval::Value::iConvert(ir::Value type, bool isSigned) const {
|
||||
using Cond = decltype([](auto type) {
|
||||
using Type = std::remove_cvref_t<decltype(type)>;
|
||||
eval::Value result;
|
||||
|
||||
return std::is_integral_v<ComponentType<Type>> &&
|
||||
!std::is_same_v<bool, ComponentType<Type>> &&
|
||||
!IsArray<decltype(type)>;
|
||||
});
|
||||
|
||||
using PairCond = decltype([](auto lhs, auto rhs) {
|
||||
using Lhs = decltype(lhs);
|
||||
using Rhs = decltype(rhs);
|
||||
|
||||
return !std::is_same_v<Lhs, Rhs> && Components<Lhs> == Components<Rhs>;
|
||||
});
|
||||
|
||||
return visitWithType<Cond, Cond, PairCond>(
|
||||
*this, type, [&](auto type, auto &&value) -> Value {
|
||||
using Type = std::remove_cvref_t<decltype(type)>;
|
||||
using ValueType = std::remove_cvref_t<decltype(value)>;
|
||||
if (isSigned) {
|
||||
return static_cast<Type>(static_cast<MakeSigned<ValueType>>(value));
|
||||
} else {
|
||||
return static_cast<Type>(static_cast<MakeUnsigned<ValueType>>(value));
|
||||
(isSigned ? makeSigned() : makeUnsigned()).visit([&](auto value) {
|
||||
invokeWithType(type, [&](auto type) {
|
||||
if constexpr (std::is_integral_v<decltype(type)> &&
|
||||
!std::is_same_v<bool, decltype(type)> &&
|
||||
std::is_integral_v<typename decltype(value)::value_type> &&
|
||||
!std::is_same_v<bool,
|
||||
typename decltype(value)::value_type>) {
|
||||
for (auto item : value) {
|
||||
result.add(static_cast<decltype(type)>(item));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
eval::Value eval::Value::fConvert(ir::Value type) const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_floating_point_v<ComponentType<decltype(type)>> &&
|
||||
!IsArray<decltype(type)>;
|
||||
eval::Value result;
|
||||
|
||||
visit([&](auto value) {
|
||||
invokeWithType(type, [&](auto type) {
|
||||
if constexpr (std::is_floating_point_v<decltype(type)> &&
|
||||
std::is_floating_point_v<
|
||||
typename decltype(value)::value_type>) {
|
||||
for (auto item : value) {
|
||||
result.add(static_cast<decltype(type)>(item));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
using PairCond = decltype([](auto lhs, auto rhs) {
|
||||
using Lhs = decltype(lhs);
|
||||
using Rhs = decltype(rhs);
|
||||
|
||||
return !std::is_same_v<Lhs, Rhs> && Components<Lhs> == Components<Rhs>;
|
||||
});
|
||||
|
||||
return visitWithType<void, void, PairCond>(
|
||||
*this, type, [&](auto type, auto &&value) -> Value {
|
||||
using Type = std::remove_cvref_t<decltype(type)>;
|
||||
return static_cast<Type>(value);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
eval::Value eval::Value::bitcast(ir::Value type) const {
|
||||
using Cond = decltype([](auto type, auto value) {
|
||||
using Type = std::remove_cvref_t<decltype(type)>;
|
||||
eval::Value result;
|
||||
|
||||
return sizeof(type) == sizeof(value);
|
||||
auto resultTypeElemCount = getIrTypeConstituents(type);
|
||||
visit([&](auto value) {
|
||||
invokeWithType(type, [&](auto resultType) {
|
||||
if (value.size_bytes() == sizeof(resultType) * resultTypeElemCount) {
|
||||
result.mTypeIndex = getIrTypeIndex(type);
|
||||
result.mCount = resultTypeElemCount;
|
||||
std::memcpy(result.mData, value.data(), value.size_bytes());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return visitWithType<void, void, Cond>(
|
||||
*this, type, [](auto type, auto &&value) -> Value {
|
||||
return std::bit_cast<decltype(type)>(value);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::uint64_t> eval::Value::zExtScalar() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_integral_v<ComponentType<decltype(type)>> &&
|
||||
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
Components<decltype(type)> == 1 && !IsArray<decltype(type)>;
|
||||
});
|
||||
|
||||
auto result = visit<Cond>(*this, [&](auto value) -> Value {
|
||||
return static_cast<std::uint64_t>(
|
||||
static_cast<MakeUnsigned<decltype(value)>>(value));
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return result.as<std::uint64_t>();
|
||||
if (empty() || size() != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
return makeUnsigned().visit([](auto value) -> std::optional<std::uint64_t> {
|
||||
if constexpr (std::is_integral_v<typename decltype(value)::value_type>) {
|
||||
return static_cast<std::uint64_t>(value[0]);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<std::int64_t> eval::Value::sExtScalar() const {
|
||||
using Cond = decltype([](auto type) {
|
||||
return std::is_integral_v<ComponentType<decltype(type)>> &&
|
||||
!std::is_same_v<ComponentType<decltype(type)>, bool> &&
|
||||
Components<decltype(type)> == 1 && !IsArray<decltype(type)>;
|
||||
});
|
||||
|
||||
auto result = visit<Cond>(*this, [&](auto value) -> Value {
|
||||
return static_cast<std::int64_t>(
|
||||
static_cast<MakeSigned<decltype(value)>>(value));
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return result.as<std::int64_t>();
|
||||
if (empty() || size() != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
return makeSigned().visit([](auto value) -> std::optional<std::int64_t> {
|
||||
if constexpr (std::is_integral_v<typename decltype(value)::value_type>) {
|
||||
return static_cast<std::int64_t>(value[0]);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#define DEFINE_BINARY_OP(OP) \
|
||||
eval::Value eval::Value::operator OP(const Value &rhs) const { \
|
||||
using LhsCond = decltype([](auto &&lhs) { return true; }); \
|
||||
return visit<LhsCond>(*this, [&]<typename Lhs>(Lhs &&lhs) -> Value { \
|
||||
using RhsCond = decltype([](auto &&rhs) { \
|
||||
return requires(Lhs lhs) { static_cast<Value>(lhs OP rhs); }; \
|
||||
}); \
|
||||
return visit<RhsCond>(rhs, [&](auto &&rhs) -> Value { \
|
||||
return static_cast<Value>(lhs OP rhs); \
|
||||
if (index() != rhs.index() || size() != rhs.size()) { \
|
||||
return {}; \
|
||||
} \
|
||||
eval::Value result; \
|
||||
visit([&](auto lhsValues) { \
|
||||
rhs.visit([&](auto rhsValues) { \
|
||||
if constexpr (requires { lhsValues[0] OP rhsValues[0]; }) { \
|
||||
if constexpr (std::is_same_v<decltype(lhsValues[0]), \
|
||||
decltype(rhsValues[0])>) { \
|
||||
for (std::size_t i = 0; i < lhsValues.size(); ++i) { \
|
||||
result.add(lhsValues[i] OP rhsValues[i]); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}); \
|
||||
}); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
#define DEFINE_UNARY_OP(OP) \
|
||||
eval::Value eval::Value::operator OP() const { \
|
||||
using Cond = decltype([](auto rhs) { \
|
||||
return requires { static_cast<Value>(OP rhs); }; \
|
||||
}); \
|
||||
return visit<Cond>(*this, [&](auto &&rhs) -> Value { \
|
||||
return static_cast<Value>(OP rhs); \
|
||||
eval::Value result; \
|
||||
visit([&](auto values) { \
|
||||
if constexpr (requires { OP values[0]; }) { \
|
||||
for (std::size_t i = 0; i < values.size(); ++i) { \
|
||||
result.add(OP values[i]); \
|
||||
} \
|
||||
} \
|
||||
}); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_BINARY_OP(+);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
#include "transform/route.hpp"
|
||||
#include "ir/Block.hpp"
|
||||
#include "transform/merge.hpp"
|
||||
#include "SpvConverter.hpp"
|
||||
#include "analyze.hpp"
|
||||
#include "dialect.hpp"
|
||||
#include "ir/Block.hpp"
|
||||
#include "transform/merge.hpp"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <rx/die.hpp>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
|
@ -22,74 +22,64 @@ using Builder = ir::Builder<ir::builtin::Builder, ir::spv::Builder>;
|
|||
struct RouteBlockData {
|
||||
std::unordered_map<ir::Block, std::unordered_set<unsigned>> fromSuccessors;
|
||||
std::unordered_map<ir::Block, std::unordered_set<ir::Block>> toPredecessors;
|
||||
std::unordered_map<ir::Block, std::unordered_set<ir::Block>> toAllPredecessors;
|
||||
std::unordered_map<ir::Block, std::unordered_set<ir::Block>>
|
||||
toAllPredecessors;
|
||||
std::unordered_set<ir::Block> patchPredecessors;
|
||||
};
|
||||
|
||||
// Helper function to get block name or generate one
|
||||
static std::string getBlockName(spv::Context& context, ir::Block block) {
|
||||
auto label = block.getFirst();
|
||||
auto name = context.ns.tryGetNameOf(label);
|
||||
return name.empty()
|
||||
? "unnamed_" + std::to_string((std::uint32_t)label.getInstId())
|
||||
: std::string(name);
|
||||
}
|
||||
|
||||
// Log detailed information about phi nodes and their predecessors
|
||||
static void logPhiPredecessorsMismatch(spv::Context& context, ir::Block to, ir::Instruction firstInst) {
|
||||
static void logPhiPredecessorsMismatch(spv::Context &context, ir::Block to) {
|
||||
// Get block label and name
|
||||
auto blockName = getBlockName(context, to);
|
||||
auto predsCount = getPredecessors(to).size();
|
||||
|
||||
for (auto phi = firstInst; phi && (phi == ir::spv::OpPhi); phi = phi.getNext()) {
|
||||
std::cerr << "[DEBUG] Block '" << blockName << "' (ID: " << (std::uint32_t)to.getInstId() << ") has " << predsCount << " predecessors";
|
||||
|
||||
for (auto phi : ir::range(to.getFirst())) {
|
||||
if (phi != ir::spv::OpPhi) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Get number of incoming blocks from phi node
|
||||
auto incomingCount = phi.getOperandCount() / 2;
|
||||
|
||||
if (incomingCount == predsCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Log mismatch if counts differ
|
||||
if (incomingCount != predsCount) {
|
||||
std::cerr << "\n Phi ID: " << (std::uint32_t)phi.getInstId() << ", incoming blocks: " << incomingCount;
|
||||
std::cerr << " *** MISMATCH! Expected: " << predsCount << " ***\n\n";
|
||||
|
||||
// Detailed phi node information
|
||||
std::cerr << " Phi: ";
|
||||
phi.print(std::cerr, context.ns);
|
||||
std::cerr << "\n";
|
||||
std::cerr << "[DEBUG] Block '" << context.ns.getNameOf(to) << "' has "
|
||||
<< predsCount << " predecessors\n"
|
||||
<< "incoming blocks: " << incomingCount;
|
||||
std::cerr << " *** MISMATCH! Expected: " << predsCount << " ***\n\n";
|
||||
|
||||
// Print detailed incoming blocks
|
||||
std::stringstream phiOperands;
|
||||
auto opts = PrintOptions().nextLevel();
|
||||
phiOperands << " Value-Blocks: [\n";
|
||||
// Detailed phi node information
|
||||
std::cerr << " Phi: ";
|
||||
phi.print(std::cerr, context.ns);
|
||||
std::cerr << "\n";
|
||||
|
||||
for (std::size_t i = 1; i < phi.getOperandCount(); i += 2) {
|
||||
auto value = phi.getOperand(i + 0).getAsValue();
|
||||
auto block = phi.getOperand(i + 1).getAsValue().staticCast<ir::Block>();
|
||||
// Print detailed incoming blocks
|
||||
std::stringstream phiOperands;
|
||||
auto opts = PrintOptions{.identLevel = 3};
|
||||
phiOperands << " Value-Blocks: [\n";
|
||||
|
||||
phiOperands << " ";
|
||||
value.print(phiOperands, context.ns, opts.nextLevel());
|
||||
phiOperands << "\n ";
|
||||
block.print(phiOperands, context.ns, opts.nextLevel());
|
||||
for (std::size_t i = 1; i < phi.getOperandCount(); i += 2) {
|
||||
auto value = phi.getOperand(i + 0).getAsValue();
|
||||
auto block = phi.getOperand(i + 1).getAsValue();
|
||||
|
||||
value.print(phiOperands, context.ns, opts);
|
||||
phiOperands << "\n";
|
||||
block.print(phiOperands, context.ns, opts);
|
||||
if (i != phi.getOperandCount() - 1) {
|
||||
phiOperands << ",\n\n";
|
||||
}
|
||||
|
||||
auto str = phiOperands.str();
|
||||
if (str.size() >= 3) {
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
}
|
||||
|
||||
std::cerr << str << "]\n";
|
||||
}
|
||||
else {
|
||||
std::cerr << " and matching incoming blocks\n";
|
||||
}
|
||||
|
||||
std::cerr << phiOperands.str() << "]\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze edges and build routing data structures
|
||||
static RouteBlockData analyzeEdges(spv::Context &context, const std::vector<Edge> &edges) {
|
||||
static RouteBlockData analyzeEdges(spv::Context &context,
|
||||
const std::vector<Edge> &edges) {
|
||||
RouteBlockData data;
|
||||
std::unordered_set<ir::Block> routePredecessors;
|
||||
|
||||
|
|
@ -107,25 +97,25 @@ static RouteBlockData analyzeEdges(spv::Context &context, const std::vector<Edge
|
|||
}
|
||||
|
||||
// Debug logging for mismatches
|
||||
for (auto& [to, _] : data.toPredecessors) {
|
||||
logPhiPredecessorsMismatch(context, to, ir::Block(to).getFirst());
|
||||
for (auto &[to, _] : data.toPredecessors) {
|
||||
logPhiPredecessorsMismatch(context, to);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Create route block with appropriate phi node
|
||||
static std::pair<ir::Block, ir::Value> createRouteBlockWithPhi(
|
||||
spv::Context &context, ir::InsertionPoint insertPoint,
|
||||
ir::Location loc, size_t predsCount) {
|
||||
static std::pair<ir::Block, ir::Value>
|
||||
createRouteBlockWithPhi(spv::Context &context, ir::InsertionPoint insertPoint,
|
||||
ir::Location loc, size_t predsCount) {
|
||||
auto route = Builder::create(context, insertPoint).createBlock(loc);
|
||||
ir::Value routePhi;
|
||||
|
||||
if (predsCount > 1) {
|
||||
routePhi = Builder::createPrepend(context, route)
|
||||
.createSpvPhi(loc, predsCount == 2
|
||||
? context.getTypeBool()
|
||||
: context.getTypeUInt32());
|
||||
routePhi =
|
||||
Builder::createPrepend(context, route)
|
||||
.createSpvPhi(loc, predsCount == 2 ? context.getTypeBool()
|
||||
: context.getTypeUInt32());
|
||||
}
|
||||
|
||||
return {route, routePhi};
|
||||
|
|
@ -154,8 +144,9 @@ static std::unordered_map<ir::Value, std::uint32_t> createRouteTerminator(
|
|||
secondSuccessor);
|
||||
} else {
|
||||
// Multiple successors: switch statement
|
||||
auto routeSwitch = Builder::createAppend(context, route)
|
||||
.createSpvSwitch(loc, routePhi, toPreds.begin()->first);
|
||||
auto routeSwitch =
|
||||
Builder::createAppend(context, route)
|
||||
.createSpvSwitch(loc, routePhi, toPreds.begin()->first);
|
||||
|
||||
successorToId.reserve(toPreds.size());
|
||||
|
||||
|
|
@ -174,8 +165,7 @@ static std::unordered_map<ir::Value, std::uint32_t> createRouteTerminator(
|
|||
// Get successor ID based on routing strategy
|
||||
static ir::Value getSuccessorIdValue(
|
||||
spv::Context &context, ir::Block successor,
|
||||
const std::unordered_map<ir::Block, std::unordered_set<ir::Block>>
|
||||
&toPreds,
|
||||
const std::unordered_map<ir::Block, std::unordered_set<ir::Block>> &toPreds,
|
||||
const std::unordered_map<ir::Value, std::uint32_t> &successorToId) {
|
||||
if (toPreds.size() == 2) {
|
||||
return context.getBool(successor == toPreds.begin()->first);
|
||||
|
|
@ -189,7 +179,7 @@ static void patchPredecessorBlock(
|
|||
ir::Value routePhi, const RouteBlockData &data,
|
||||
const std::unordered_map<ir::Block, std::unordered_set<ir::Block>> &toPreds,
|
||||
const std::function<ir::Value(ir::Block)> &getSuccessorId) {
|
||||
|
||||
|
||||
auto predSuccessors = getAllSuccessors(patchBlock);
|
||||
auto terminator = getTerminator(patchBlock);
|
||||
auto &routeSuccessors = data.fromSuccessors.at(patchBlock);
|
||||
|
|
@ -317,7 +307,8 @@ static void patchPredecessorBlock(
|
|||
}
|
||||
|
||||
// Move all phi nodes from target to route block
|
||||
static void moveAllPhiNodes(spv::Context &context, ir::Block to, ir::Block route,
|
||||
static void moveAllPhiNodes(spv::Context &context, ir::Block to,
|
||||
ir::Block route,
|
||||
const std::unordered_set<ir::Block> &preds,
|
||||
const std::vector<Edge> &edges) {
|
||||
for (auto phi : ir::range(ir::Block(to).getFirst())) {
|
||||
|
|
@ -413,7 +404,7 @@ static void processTargetBlocks(
|
|||
const std::unordered_map<ir::Block, std::unordered_set<ir::Block>> &toPreds,
|
||||
const std::vector<Edge> &edges,
|
||||
const std::function<ir::Value(ir::Block)> &getSuccessorId) {
|
||||
|
||||
|
||||
for (auto &[to, preds] : toPreds) {
|
||||
if (toPreds.size() > 1) {
|
||||
auto successorId = getSuccessorId(to);
|
||||
|
|
@ -456,8 +447,8 @@ static void processTargetBlocks(
|
|||
|
||||
// Main function
|
||||
ir::Block shader::transform::createRouteBlock(spv::Context &context,
|
||||
ir::InsertionPoint insertPoint,
|
||||
const std::vector<Edge> &edges) {
|
||||
ir::InsertionPoint insertPoint,
|
||||
const std::vector<Edge> &edges) {
|
||||
auto loc = context.getUnknownLocation();
|
||||
|
||||
rx::dieIf(edges.empty(), "createRouteBlock: unexpected edges count");
|
||||
|
|
@ -472,27 +463,28 @@ ir::Block shader::transform::createRouteBlock(spv::Context &context,
|
|||
}
|
||||
|
||||
// Step 3: Create route block and phi node
|
||||
auto [route, routePhi] = createRouteBlockWithPhi(context, insertPoint,
|
||||
loc, data.toPredecessors.size());
|
||||
auto [route, routePhi] = createRouteBlockWithPhi(context, insertPoint, loc,
|
||||
data.toPredecessors.size());
|
||||
|
||||
// Step 4: Create appropriate terminator (branch/conditional/switch)
|
||||
auto successorToId = createRouteTerminator(context, route, routePhi,
|
||||
loc, data.toPredecessors);
|
||||
auto successorToId =
|
||||
createRouteTerminator(context, route, routePhi, loc, data.toPredecessors);
|
||||
|
||||
// Step 5: Create lambda for getting successor IDs
|
||||
auto getSuccessorId = [&](ir::Block successor) {
|
||||
return getSuccessorIdValue(context, successor, data.toPredecessors, successorToId);
|
||||
return getSuccessorIdValue(context, successor, data.toPredecessors,
|
||||
successorToId);
|
||||
};
|
||||
|
||||
// Step 6: Patch predecessor blocks that have multiple routes
|
||||
for (auto patchBlock : data.patchPredecessors) {
|
||||
patchPredecessorBlock(context, patchBlock, route, routePhi, data,
|
||||
data.toPredecessors, getSuccessorId);
|
||||
data.toPredecessors, getSuccessorId);
|
||||
}
|
||||
|
||||
// Step 7: Process target blocks and update phi nodes
|
||||
processTargetBlocks(context, route, routePhi, data, data.toPredecessors, edges,
|
||||
getSuccessorId);
|
||||
processTargetBlocks(context, route, routePhi, data, data.toPredecessors,
|
||||
edges, getSuccessorId);
|
||||
|
||||
return route;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -469,6 +469,22 @@ static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
|
|||
return {};
|
||||
});
|
||||
|
||||
} else if (args->id == 0xb) {
|
||||
struct VblankStatus {
|
||||
orbis::uint64_t count;
|
||||
orbis::uint64_t processTime;
|
||||
orbis::uint64_t tsc;
|
||||
char flags;
|
||||
};
|
||||
|
||||
VblankStatus vblankStatus{};
|
||||
// TODO: lock bridge header
|
||||
vblankStatus.count = gpuCtx.vblankCount;
|
||||
vblankStatus.processTime = 0; // TODO
|
||||
vblankStatus.tsc = 0; // TODO
|
||||
vblankStatus.flags = 0; // TODO
|
||||
|
||||
std::memcpy(args->ptr, &vblankStatus, sizeof(VblankStatus));
|
||||
} else { // used during open/close
|
||||
ORBIS_LOG_NOTICE("dce: UNIMPLEMENTED FlipControl", args->id, args->arg2,
|
||||
args->ptr, args->size);
|
||||
|
|
|
|||
Loading…
Reference in a new issue