SpirvShader: Implement GLSLstd450Determinant

Bug: b/126873455
Tests: dEQP-VK.glsl.matrix.determinant.*
Change-Id: I81ae56951b9fa2e1aedb327039160cf44f7f0a18
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28728
Tested-by: Ben Clayton <bclayton@google.com>
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 3c785cf..e79fa94 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -103,6 +103,44 @@
 			((~xIsNan &  yIsNan) & As<sw::SIMD::Int>(x)) |
 			(( xIsNan          ) & As<sw::SIMD::Int>(y)));
 	}
+
+	// Returns the determinant of a 2x2 matrix.
+	rr::RValue<sw::SIMD::Float> Determinant(
+		rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b,
+		rr::RValue<sw::SIMD::Float> const &c, rr::RValue<sw::SIMD::Float> const &d)
+	{
+		return a*d - b*c;
+	}
+
+	// Returns the determinant of a 3x3 matrix.
+	rr::RValue<sw::SIMD::Float> Determinant(
+		rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b, rr::RValue<sw::SIMD::Float> const &c,
+		rr::RValue<sw::SIMD::Float> const &d, rr::RValue<sw::SIMD::Float> const &e, rr::RValue<sw::SIMD::Float> const &f,
+		rr::RValue<sw::SIMD::Float> const &g, rr::RValue<sw::SIMD::Float> const &h, rr::RValue<sw::SIMD::Float> const &i)
+	{
+		return a*e*i + b*f*g + c*d*h - c*e*g - b*d*i - a*f*h;
+	}
+
+	// Returns the determinant of a 4x4 matrix.
+	rr::RValue<sw::SIMD::Float> Determinant(
+		rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b, rr::RValue<sw::SIMD::Float> const &c, rr::RValue<sw::SIMD::Float> const &d,
+		rr::RValue<sw::SIMD::Float> const &e, rr::RValue<sw::SIMD::Float> const &f, rr::RValue<sw::SIMD::Float> const &g, rr::RValue<sw::SIMD::Float> const &h,
+		rr::RValue<sw::SIMD::Float> const &i, rr::RValue<sw::SIMD::Float> const &j, rr::RValue<sw::SIMD::Float> const &k, rr::RValue<sw::SIMD::Float> const &l,
+		rr::RValue<sw::SIMD::Float> const &m, rr::RValue<sw::SIMD::Float> const &n, rr::RValue<sw::SIMD::Float> const &o, rr::RValue<sw::SIMD::Float> const &p)
+	{
+		return a * Determinant(f, g, h,
+		                       j, k, l,
+		                       n, o, p) -
+		       b * Determinant(e, g, h,
+		                       i, k, l,
+		                       m, o, p) +
+		       c * Determinant(e, f, h,
+		                       i, j, l,
+		                       m, n, p) -
+		       d * Determinant(e, f, g,
+		                       i, j, k,
+		                       m, n, o);
+	}
 }
 
 namespace sw
@@ -3518,7 +3556,31 @@
 		}
 		case GLSLstd450Determinant:
 		{
-			UNIMPLEMENTED("GLSLstd450Determinant");
+			auto mat = GenericValue(this, routine, insn.word(5));
+			auto numComponents = getType(mat.type).sizeInComponents;
+			switch (numComponents)
+			{
+			case 4: // 2x2
+				dst.move(0, Determinant(
+					mat.Float(0), mat.Float(1),
+					mat.Float(2), mat.Float(3)));
+				break;
+			case 9: // 3x3
+				dst.move(0, Determinant(
+					mat.Float(0), mat.Float(1), mat.Float(2),
+					mat.Float(3), mat.Float(4), mat.Float(5),
+					mat.Float(6), mat.Float(7), mat.Float(8)));
+				break;
+			case 16: // 4x4
+				dst.move(0, Determinant(
+					mat.Float(0),  mat.Float(1),  mat.Float(2),  mat.Float(3),
+					mat.Float(4),  mat.Float(5),  mat.Float(6),  mat.Float(7),
+					mat.Float(8),  mat.Float(9),  mat.Float(10), mat.Float(11),
+					mat.Float(12), mat.Float(13), mat.Float(14), mat.Float(15)));
+				break;
+			default:
+				UNREACHABLE("GLSLstd450Determinant can only operate with square matrices. Got %d elements", int(numComponents));
+			}
 			break;
 		}
 		case GLSLstd450MatrixInverse: