ソースを参照

spirv: Add integer clamping workarounds

Workaround more bugs on Nvidia's OpenGL SPIR-V compiler.
ReinUsesLisp 5 年 前
コミット
33bebc3412
1 ファイル変更34 行追加4 行削除
  1. 34 4
      src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp

+ 34 - 4
src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp

@@ -171,7 +171,13 @@ Id EmitFindUMsb32(EmitContext& ctx, Id value) {
 }
 
 Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
-    return ctx.OpSMin(ctx.U32[1], a, b);
+    const bool is_broken{ctx.profile.has_broken_signed_operations};
+    if (is_broken) {
+        a = ctx.OpBitcast(ctx.S32[1], a);
+        b = ctx.OpBitcast(ctx.S32[1], b);
+    }
+    const Id result{ctx.OpSMin(ctx.U32[1], a, b)};
+    return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
 }
 
 Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
@@ -179,7 +185,13 @@ Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
 }
 
 Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
-    return ctx.OpSMax(ctx.U32[1], a, b);
+    const bool is_broken{ctx.profile.has_broken_signed_operations};
+    if (is_broken) {
+        a = ctx.OpBitcast(ctx.S32[1], a);
+        b = ctx.OpBitcast(ctx.S32[1], b);
+    }
+    const Id result{ctx.OpSMax(ctx.U32[1], a, b)};
+    return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result;
 }
 
 Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
@@ -187,14 +199,32 @@ Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
 }
 
 Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
-    const Id result{ctx.OpSClamp(ctx.U32[1], value, min, max)};
+    Id result{};
+    if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) {
+        value = ctx.OpBitcast(ctx.S32[1], value);
+        min = ctx.OpBitcast(ctx.S32[1], min);
+        max = ctx.OpBitcast(ctx.S32[1], max);
+        if (ctx.profile.has_broken_spirv_clamp) {
+            result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
+        } else {
+            result = ctx.OpSClamp(ctx.S32[1], value, min, max);
+        }
+        result = ctx.OpBitcast(ctx.U32[1], result);
+    } else {
+        result = ctx.OpSClamp(ctx.U32[1], value, min, max);
+    }
     SetZeroFlag(ctx, inst, result);
     SetSignFlag(ctx, inst, result);
     return result;
 }
 
 Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
-    const Id result{ctx.OpUClamp(ctx.U32[1], value, min, max)};
+    Id result{};
+    if (ctx.profile.has_broken_spirv_clamp) {
+        result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
+    } else {
+        result = ctx.OpUClamp(ctx.U32[1], value, min, max);
+    }
     SetZeroFlag(ctx, inst, result);
     SetSignFlag(ctx, inst, result);
     return result;