mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
spirv: Fixes and Intel specific workarounds
This commit is contained in:
parent
704c6f353f
commit
274897dfd5
11 changed files with 44 additions and 32 deletions
|
@ -25,7 +25,8 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitContext::EmitContext(IR::Program& program) : Sirit::Module(0x00010000) {
|
EmitContext::EmitContext(const Profile& profile_, IR::Program& program)
|
||||||
|
: Sirit::Module(0x00010000), profile{profile_} {
|
||||||
AddCapability(spv::Capability::Shader);
|
AddCapability(spv::Capability::Shader);
|
||||||
DefineCommonTypes(program.info);
|
DefineCommonTypes(program.info);
|
||||||
DefineCommonConstants();
|
DefineCommonConstants();
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "shader_recompiler/frontend/ir/program.h"
|
#include "shader_recompiler/frontend/ir/program.h"
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
|
#include "shader_recompiler/profile.h"
|
||||||
|
|
||||||
namespace Shader::Backend::SPIRV {
|
namespace Shader::Backend::SPIRV {
|
||||||
|
|
||||||
|
@ -30,11 +31,13 @@ private:
|
||||||
|
|
||||||
class EmitContext final : public Sirit::Module {
|
class EmitContext final : public Sirit::Module {
|
||||||
public:
|
public:
|
||||||
explicit EmitContext(IR::Program& program);
|
explicit EmitContext(const Profile& profile, IR::Program& program);
|
||||||
~EmitContext();
|
~EmitContext();
|
||||||
|
|
||||||
[[nodiscard]] Id Def(const IR::Value& value);
|
[[nodiscard]] Id Def(const IR::Value& value);
|
||||||
|
|
||||||
|
const Profile& profile;
|
||||||
|
|
||||||
Id void_id{};
|
Id void_id{};
|
||||||
Id U1{};
|
Id U1{};
|
||||||
Id U16{};
|
Id U16{};
|
||||||
|
|
|
@ -150,11 +150,11 @@ void SetupDenormControl(const Profile& profile, const IR::Program& program, Emit
|
||||||
} else if (info.uses_fp16_denorms_flush) {
|
} else if (info.uses_fp16_denorms_flush) {
|
||||||
if (profile.support_fp16_denorm_flush) {
|
if (profile.support_fp16_denorm_flush) {
|
||||||
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
||||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U);
|
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 16U);
|
||||||
} else {
|
} else {
|
||||||
// Same as fp32, no need to warn as most drivers will flush by default
|
// Same as fp32, no need to warn as most drivers will flush by default
|
||||||
}
|
}
|
||||||
} else if (info.uses_fp32_denorms_preserve) {
|
} else if (info.uses_fp16_denorms_preserve) {
|
||||||
if (profile.support_fp16_denorm_preserve) {
|
if (profile.support_fp16_denorm_preserve) {
|
||||||
ctx.AddCapability(spv::Capability::DenormPreserve);
|
ctx.AddCapability(spv::Capability::DenormPreserve);
|
||||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U);
|
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U);
|
||||||
|
@ -166,7 +166,7 @@ void SetupDenormControl(const Profile& profile, const IR::Program& program, Emit
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program) {
|
std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program) {
|
||||||
EmitContext ctx{program};
|
EmitContext ctx{profile, program};
|
||||||
const Id void_function{ctx.TypeFunction(ctx.void_id)};
|
const Id void_function{ctx.TypeFunction(ctx.void_id)};
|
||||||
// FIXME: Forward declare functions (needs sirit support)
|
// FIXME: Forward declare functions (needs sirit support)
|
||||||
Id func{};
|
Id func{};
|
||||||
|
|
|
@ -202,10 +202,10 @@ Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||||
Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs);
|
Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||||
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||||
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||||
void EmitLogicalOr(EmitContext& ctx);
|
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b);
|
||||||
void EmitLogicalAnd(EmitContext& ctx);
|
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b);
|
||||||
void EmitLogicalXor(EmitContext& ctx);
|
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b);
|
||||||
void EmitLogicalNot(EmitContext& ctx);
|
Id EmitLogicalNot(EmitContext& ctx, Id value);
|
||||||
Id EmitConvertS16F16(EmitContext& ctx, Id value);
|
Id EmitConvertS16F16(EmitContext& ctx, Id value);
|
||||||
Id EmitConvertS16F32(EmitContext& ctx, Id value);
|
Id EmitConvertS16F32(EmitContext& ctx, Id value);
|
||||||
Id EmitConvertS16F64(EmitContext& ctx, Id value);
|
Id EmitConvertS16F64(EmitContext& ctx, Id value);
|
||||||
|
|
|
@ -15,6 +15,13 @@ Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Id Saturate(EmitContext& ctx, Id type, Id value, Id zero, Id one) {
|
||||||
|
if (ctx.profile.has_broken_spirv_clamp) {
|
||||||
|
return ctx.OpFMin(type, ctx.OpFMax(type, value, zero), one);
|
||||||
|
} else {
|
||||||
|
return ctx.OpFClamp(type, value, zero, one);
|
||||||
|
}
|
||||||
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Id EmitFPAbs16(EmitContext& ctx, Id value) {
|
Id EmitFPAbs16(EmitContext& ctx, Id value) {
|
||||||
|
@ -144,19 +151,19 @@ void EmitFPLog2(EmitContext&) {
|
||||||
Id EmitFPSaturate16(EmitContext& ctx, Id value) {
|
Id EmitFPSaturate16(EmitContext& ctx, Id value) {
|
||||||
const Id zero{ctx.Constant(ctx.F16[1], u16{0})};
|
const Id zero{ctx.Constant(ctx.F16[1], u16{0})};
|
||||||
const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})};
|
const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})};
|
||||||
return ctx.OpFClamp(ctx.F32[1], value, zero, one);
|
return Saturate(ctx, ctx.F16[1], value, zero, one);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitFPSaturate32(EmitContext& ctx, Id value) {
|
Id EmitFPSaturate32(EmitContext& ctx, Id value) {
|
||||||
const Id zero{ctx.Constant(ctx.F32[1], f32{0.0})};
|
const Id zero{ctx.Constant(ctx.F32[1], f32{0.0})};
|
||||||
const Id one{ctx.Constant(ctx.F32[1], f32{1.0})};
|
const Id one{ctx.Constant(ctx.F32[1], f32{1.0})};
|
||||||
return ctx.OpFClamp(ctx.F32[1], value, zero, one);
|
return Saturate(ctx, ctx.F32[1], value, zero, one);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitFPSaturate64(EmitContext& ctx, Id value) {
|
Id EmitFPSaturate64(EmitContext& ctx, Id value) {
|
||||||
const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})};
|
const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})};
|
||||||
const Id one{ctx.Constant(ctx.F64[1], f64{1.0})};
|
const Id one{ctx.Constant(ctx.F64[1], f64{1.0})};
|
||||||
return ctx.OpFClamp(ctx.F64[1], value, zero, one);
|
return Saturate(ctx, ctx.F64[1], value, zero, one);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitFPRoundEven16(EmitContext& ctx, Id value) {
|
Id EmitFPRoundEven16(EmitContext& ctx, Id value) {
|
||||||
|
|
|
@ -6,20 +6,20 @@
|
||||||
|
|
||||||
namespace Shader::Backend::SPIRV {
|
namespace Shader::Backend::SPIRV {
|
||||||
|
|
||||||
void EmitLogicalOr(EmitContext&) {
|
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b) {
|
||||||
throw NotImplementedException("SPIR-V Instruction");
|
return ctx.OpLogicalOr(ctx.U1, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitLogicalAnd(EmitContext&) {
|
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b) {
|
||||||
throw NotImplementedException("SPIR-V Instruction");
|
return ctx.OpLogicalAnd(ctx.U1, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitLogicalXor(EmitContext&) {
|
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b) {
|
||||||
throw NotImplementedException("SPIR-V Instruction");
|
return ctx.OpLogicalNotEqual(ctx.U1, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitLogicalNot(EmitContext&) {
|
Id EmitLogicalNot(EmitContext& ctx, Id value) {
|
||||||
throw NotImplementedException("SPIR-V Instruction");
|
return ctx.OpLogicalNot(ctx.U1, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Backend::SPIRV
|
} // namespace Shader::Backend::SPIRV
|
||||||
|
|
|
@ -272,11 +272,9 @@ public:
|
||||||
explicit GotoPass(std::span<Block* const> blocks, ObjectPool<Statement>& stmt_pool)
|
explicit GotoPass(std::span<Block* const> blocks, ObjectPool<Statement>& stmt_pool)
|
||||||
: pool{stmt_pool} {
|
: pool{stmt_pool} {
|
||||||
std::vector gotos{BuildUnorderedTreeGetGotos(blocks)};
|
std::vector gotos{BuildUnorderedTreeGetGotos(blocks)};
|
||||||
fmt::print(stdout, "BEFORE\n{}\n", DumpTree(root_stmt.children));
|
|
||||||
for (const Node& goto_stmt : gotos | std::views::reverse) {
|
for (const Node& goto_stmt : gotos | std::views::reverse) {
|
||||||
RemoveGoto(goto_stmt);
|
RemoveGoto(goto_stmt);
|
||||||
}
|
}
|
||||||
fmt::print(stdout, "AFTER\n{}\n", DumpTree(root_stmt.children));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement& RootStatement() noexcept {
|
Statement& RootStatement() noexcept {
|
||||||
|
@ -548,7 +546,6 @@ private:
|
||||||
size_t Offset(ConstNode stmt) const {
|
size_t Offset(ConstNode stmt) const {
|
||||||
size_t offset{0};
|
size_t offset{0};
|
||||||
if (!SearchNode(root_stmt.children, stmt, offset)) {
|
if (!SearchNode(root_stmt.children, stmt, offset)) {
|
||||||
fmt::print(stdout, "{}\n", DumpTree(root_stmt.children));
|
|
||||||
throw LogicError("Node not found in tree");
|
throw LogicError("Node not found in tree");
|
||||||
}
|
}
|
||||||
return offset;
|
return offset;
|
||||||
|
|
|
@ -56,7 +56,6 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||||
.post_order_blocks{},
|
.post_order_blocks{},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fmt::print(stdout, "{}\n", IR::DumpProgram(program));
|
|
||||||
Optimization::LowerFp16ToFp32(program);
|
Optimization::LowerFp16ToFp32(program);
|
||||||
for (IR::Function& function : functions) {
|
for (IR::Function& function : functions) {
|
||||||
function.post_order_blocks = PostOrder(function.blocks);
|
function.post_order_blocks = PostOrder(function.blocks);
|
||||||
|
@ -70,8 +69,6 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||||
Optimization::VerificationPass(function);
|
Optimization::VerificationPass(function);
|
||||||
}
|
}
|
||||||
Optimization::CollectShaderInfoPass(program);
|
Optimization::CollectShaderInfoPass(program);
|
||||||
|
|
||||||
fmt::print(stdout, "{}\n", IR::DumpProgram(program));
|
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,9 +83,12 @@ IR::U32 TranslatorVisitor::GetImm20(u64 insn) {
|
||||||
BitField<20, 19, u64> value;
|
BitField<20, 19, u64> value;
|
||||||
BitField<56, 1, u64> is_negative;
|
BitField<56, 1, u64> is_negative;
|
||||||
} const imm{insn};
|
} const imm{insn};
|
||||||
const s32 positive_value{static_cast<s32>(imm.value)};
|
if (imm.is_negative != 0) {
|
||||||
const s32 value{imm.is_negative != 0 ? -positive_value : positive_value};
|
const s64 raw{static_cast<s64>(imm.value)};
|
||||||
return ir.Imm32(value);
|
return ir.Imm32(static_cast<s32>(-(1LL << 19) + raw));
|
||||||
|
} else {
|
||||||
|
return ir.Imm32(static_cast<u32>(imm.value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::F32 TranslatorVisitor::GetFloatImm20(u64 insn) {
|
IR::F32 TranslatorVisitor::GetFloatImm20(u64 insn) {
|
||||||
|
@ -94,9 +97,9 @@ IR::F32 TranslatorVisitor::GetFloatImm20(u64 insn) {
|
||||||
BitField<20, 19, u64> value;
|
BitField<20, 19, u64> value;
|
||||||
BitField<56, 1, u64> is_negative;
|
BitField<56, 1, u64> is_negative;
|
||||||
} const imm{insn};
|
} const imm{insn};
|
||||||
const f32 positive_value{Common::BitCast<f32>(static_cast<u32>(imm.value) << 12)};
|
const u32 sign_bit{imm.is_negative != 0 ? (1ULL << 31) : 0};
|
||||||
const f32 value{imm.is_negative != 0 ? -positive_value : positive_value};
|
const u32 value{static_cast<u32>(imm.value) << 12};
|
||||||
return ir.Imm32(value);
|
return ir.Imm32(Common::BitCast<f32>(value | sign_bit));
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 TranslatorVisitor::GetImm32(u64 insn) {
|
IR::U32 TranslatorVisitor::GetImm32(u64 insn) {
|
||||||
|
|
|
@ -15,6 +15,9 @@ struct Profile {
|
||||||
bool support_fp32_denorm_preserve{};
|
bool support_fp32_denorm_preserve{};
|
||||||
bool support_fp16_denorm_flush{};
|
bool support_fp16_denorm_flush{};
|
||||||
bool support_fp32_denorm_flush{};
|
bool support_fp32_denorm_flush{};
|
||||||
|
|
||||||
|
// FClamp is broken and OpFMax + OpFMin should be used instead
|
||||||
|
bool has_broken_spirv_clamp{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
|
|
@ -189,6 +189,7 @@ ComputePipeline PipelineCache::CreateComputePipeline(ShaderInfo* shader_info) {
|
||||||
.support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE,
|
.support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE,
|
||||||
.support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
|
.support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
|
||||||
.support_fp32_denorm_flush = float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE,
|
.support_fp32_denorm_flush = float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE,
|
||||||
|
.has_broken_spirv_clamp = true, // TODO: is_intel
|
||||||
};
|
};
|
||||||
const auto [info, code]{Shader::RecompileSPIRV(profile, env, qmd.program_start)};
|
const auto [info, code]{Shader::RecompileSPIRV(profile, env, qmd.program_start)};
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue