Browse Source

Adding DirectX12 shader cache support. Can create shader cache for XBox series using dxc.exe.

clementlandrin 1 year ago
parent
commit
f4979ea946
2 changed files with 117 additions and 10 deletions
  1. 75 8
      h3d/impl/DX12Driver.hx
  2. 42 2
      hxsl/CacheFileBuilder.hx

+ 75 - 8
h3d/impl/DX12Driver.hx

@@ -867,12 +867,30 @@ class DX12Driver extends h3d.impl.Driver {
 
 	static var VERTEX_FORMATS = [null,null,R32G32_FLOAT,R32G32B32_FLOAT,R32G32B32A32_FLOAT];
 
+	function getBinaryPayload( vertex : Bool, code : String ) {
+		var bin = code.indexOf("//BIN=");
+		if( bin >= 0 ) {
+			var end = code.indexOf("#", bin);
+			if( end >= 0 )
+				return haxe.crypto.Base64.decode(code.substr(bin + 6, end - bin - 6));
+		}
+		if( shaderCache != null )
+			return shaderCache.resolveShaderBinary(code);
+		return null;
+	}
+
 	function compileSource( sh : hxsl.RuntimeShader.RuntimeShaderData, profile, baseRegister ) {
 		var args = [];
 		var out = new hxsl.HlslOut();
 		out.baseRegister = baseRegister;
-		var source = out.run(sh.data);
-		return compiler.compile(source, profile, args);
+		if ( sh.code == null ) {
+			sh.code = out.run(sh.data);
+		}
+		var bytes = getBinaryPayload(sh.vertex, sh.code);
+		if ( bytes == null ) {
+			return compiler.compile(sh.code, profile, args);
+		}
+		return bytes;
 	}
 
 	override function getNativeShaderCode( shader : hxsl.RuntimeShader ) {
@@ -883,6 +901,61 @@ class DX12Driver extends h3d.impl.Driver {
 		return vsSource+"\n\n\n\n"+psSource;
 	}
 
+	function stringifyRootSignature( sign : RootSignatureDesc, name : String, params : hl.CArray<RootParameterConstants> ) : String {
+		var s = '#define ${name} "RootFlags(';
+		if ( sign.flags.toInt() == 0 )
+			s += '0'; // no flags
+		else {
+			// RootFlags
+			for ( f in haxe.EnumTools.getConstructors(RootSignatureFlag) ) {
+				if ( !sign.flags.has(haxe.EnumTools.createByName(RootSignatureFlag, f)) )
+					continue;
+				s += Std.string(f) + '|';
+			}
+			s = s.substr(0, s.length - 1);
+		}
+		s += ')",';
+
+		for ( param in params ) {
+			var vis = 'SHADER_VISIBILITY_${param.shaderVisibility == VERTEX ? "VERTEX" : "PIXEL"}';
+			if ( param.parameterType == CONSTANTS ) {
+				var shaderRegister = param.shaderRegister;
+				s += 'RootConstants(num32BitConstants=${param.num32BitValues},b${shaderRegister}, visibility=${vis}),';
+			} else {
+				try {
+					var p = unsafeCastTo(param, RootParameterDescriptorTable);
+					if ( p == null ) continue;
+					var descRange = p.descriptorRanges;
+					if ( descRange == null ) continue;
+					var baseShaderRegister = descRange.baseShaderRegister;
+					switch ( descRange.rangeType) {
+					case CBV:
+						s += 'DescriptorTable(CBV(b${baseShaderRegister}), visibility = ${vis}),';
+					case SRV:
+						s += 'DescriptorTable(SRV(t${baseShaderRegister},numDescriptors = ${descRange.numDescriptors}), visibility = ${vis}),';
+					case SAMPLER:
+						var baseShaderRegister = descRange.baseShaderRegister;
+						s += 'DescriptorTable(Sampler(s${baseShaderRegister}, space=${descRange.registerSpace}, numDescriptors = ${descRange.numDescriptors}), visibility = ${vis}),';
+					case UAV:
+						throw "Not supported";
+					}
+				} catch ( e : Dynamic ) {
+					continue;
+				}
+			}
+		}
+
+		s += '\n';
+		return s;
+	}
+
+	inline function unsafeCastTo<T,R>( v : T, c : Class<R> ) : R {
+		var arr = new hl.NativeArray<T>(1);
+		arr[0] = v;
+		return (cast arr : hl.NativeArray<R>)[0];
+	}
+
+
 	function computeRootSignature( shader : hxsl.RuntimeShader ) {
 		var params = hl.CArray.alloc(RootParameterConstants,16);
 		var paramsCount = 0, regCount = 0;
@@ -890,12 +963,6 @@ class DX12Driver extends h3d.impl.Driver {
 		var vertexParamsCBV = false;
 		var fragmentParamsCBV = false;
 
-		inline function unsafeCastTo<T,R>( v : T, c : Class<R> ) : R {
-			var arr = new hl.NativeArray<T>(1);
-			arr[0] = v;
-			return (cast arr : hl.NativeArray<R>)[0];
-		}
-
 		function allocDescTable(vis) {
 			var p = unsafeCastTo(params[paramsCount++], RootParameterDescriptorTable);
 			p.parameterType = DESCRIPTOR_TABLE;

+ 42 - 2
hxsl/CacheFileBuilder.hx

@@ -5,6 +5,7 @@ enum CacheFilePlatform {
 	OpenGL;
 	PS4;
 	XBoxOne;
+	XBoxSeries;
 	NX;
 	NXBinaries;
 }
@@ -57,6 +58,7 @@ private class CustomCacheFile extends CacheFile {
 		case OpenGL: "gl";
 		case PS4: "ps4";
 		case XBoxOne: "xboxone";
+		case XBoxSeries: "xbox";
 		case NX: "nx";
 		case NXBinaries: "nxbin";
 		};
@@ -70,7 +72,11 @@ class CacheFileBuilder {
 	public var platforms : Array<CacheFilePlatform> = [];
 	public var shaderLib : Map<String,String> = new Map();
 	public var dxInitDone = false;
+	#if (hldx && dx12)
+	public var dx12Driver : h3d.impl.DX12Driver;
+	#end
 	public var dxShaderVersion = "5_0";
+	public var dxcShaderVersion = "6_0";
 	var glout : GlslOut;
 	var vertexOut : String;
 	var hasCompiled : Bool;
@@ -116,7 +122,7 @@ class CacheFileBuilder {
 	function generateShader( r : RuntimeShader, rd : RuntimeShader.RuntimeShaderData ) : { code : String, bytes : haxe.io.Bytes } {
 		switch( platform ) {
 		case DirectX:
-			#if hldx
+			#if (hldx && !dx12)
 			if( !dxInitDone ) {
 				var win = new dx.Window("", 800, 600);
 				win.visible = false;
@@ -128,7 +134,7 @@ class CacheFileBuilder {
 			var bytes = dx.Driver.compileShader(code, "", "main", (rd.vertex?"vs_":"ps_") + dxShaderVersion, OptimizationLevel3);
 			return { code : code, bytes : bytes };
 			#else
-			throw "DirectX compilation requires -lib hldx";
+			throw "DirectX compilation requires -lib hldx without -D dx12";
 			#end
 		case OpenGL:
 			if( rd.vertex ) {
@@ -177,6 +183,38 @@ class CacheFileBuilder {
 			sys.FileSystem.deleteFile(tmpSrc);
 			sys.FileSystem.deleteFile(tmpOut);
 			return { code : code, bytes : data };
+		case XBoxSeries:
+			#if (hldx && dx12)
+			if( !dxInitDone ) {
+				var win = new dx.Window("", 800, 600);
+				win.visible = false;
+				dxInitDone = true;
+				dx12Driver = new h3d.impl.DX12Driver();
+			}
+			var out = new HlslOut();
+			var tmpFile = "tmp";
+			var tmpSrc = tmpFile + ".hlsl";
+			var tmpOut = tmpFile + ".sb";
+			var sign = @:privateAccess dx12Driver.computeRootSignature(r);
+			out.baseRegister = rd.vertex ? 0 : sign.fragmentRegStart;
+			var code = out.run(rd.data);
+			var serializeRootSignature = @:privateAccess dx12Driver.stringifyRootSignature(sign.sign, "ROOT_SIGNATURE", sign.params);
+			code = serializeRootSignature + code;
+			sys.io.File.saveContent(tmpSrc, code);
+			var args = ["-rootsig-define", "ROOT_SIGNATURE", "-T", (rd.vertex ? "vs_" : "ps_") + dxcShaderVersion,"-O3","-Fo", tmpOut, tmpSrc];
+			var p = new sys.io.Process(Sys.getEnv("GXDKLatest")+ "bin\\Scarlett\\dxc.exe", args);
+			var error = p.stderr.readAll().toString();
+			var ecode = p.exitCode();
+			if( ecode != 0 )
+				throw "ERROR while compiling " + tmpSrc + "\n" + error;
+			p.close();
+			var data = sys.io.File.getBytes(tmpOut);
+			sys.FileSystem.deleteFile(tmpSrc);
+			sys.FileSystem.deleteFile(tmpOut);
+			return { code : null, bytes : data };
+			#else
+			throw "-lib hldx and -D dx12 are required to generate binaries for XBoxSeries";
+			#end
 		case NX:
 			if( rd.vertex )
 				glout = new hxsl.NXGlslOut();
@@ -233,6 +271,8 @@ class CacheFileBuilder {
 				builder.platforms.push(PS4);
 			case "-xbox":
 				builder.platforms.push(XBoxOne);
+			case "-xbs":
+				builder.platforms.push(XBoxSeries);
 			case "-nx":
 				builder.platforms.push(NX);
 			case "-nxbinary":