public void AsmMatchesFxc(string relPath) { string file = $"{ShaderDirectory}/{relPath}"; // Arrange. var asmFileText = string.Join(Environment.NewLine, File.ReadAllLines(file + ".asm").Select(x => x.Trim())); asmFileText = TestUtils.StripDX9InstructionSlots(asmFileText); asmFileText = TestUtils.TrimLines(asmFileText); asmFileText = TestUtils.NormalizeAssembly(asmFileText); // Act. var bytecode = File.ReadAllBytes(file + ".o"); var shader = ShaderReader.ReadShader(bytecode); var decompiledAsm = AsmWriter.Disassemble(shader); File.WriteAllText($"{file}.d.asm", decompiledAsm); var decompiledAsmText = decompiledAsm; decompiledAsmText = TestUtils.StripDX9InstructionSlots(decompiledAsmText); decompiledAsmText = TestUtils.TrimLines(decompiledAsmText); decompiledAsmText = TestUtils.NormalizeAssembly(decompiledAsmText); File.WriteAllText($"{file}.d1.asm", asmFileText); File.WriteAllText($"{file}.d2.asm", decompiledAsmText); // Assert. Assert.That(decompiledAsmText, Is.EqualTo(asmFileText)); }
public void DecompileTest(string baseFilename) { string compiledShaderFilename = $"CompiledShaders{Path.DirectorySeparatorChar}{baseFilename}.fxc"; string asmExpectedFilename = $"ShaderAssembly{Path.DirectorySeparatorChar}{baseFilename}.asm"; string hlslExpectedFilename = $"ShaderSources{Path.DirectorySeparatorChar}{baseFilename}.fx"; string asmOutputFilename = $"{baseFilename}.asm"; string hlslOutputFilename = $"{baseFilename}.fx"; ShaderModel shader; var inputStream = File.Open(compiledShaderFilename, FileMode.Open, FileAccess.Read); using (var input = new ShaderReader(inputStream, true)) { shader = input.ReadShader(); } var asmWriter = new AsmWriter(shader); asmWriter.Write(asmOutputFilename); var hlslWriter = new HlslWriter(shader, true); hlslWriter.Write(hlslOutputFilename); FileAssert.AreEqual(asmExpectedFilename, asmOutputFilename, "Assembly not equal"); FileAssert.AreEqual(hlslExpectedFilename, hlslOutputFilename, "HLSL not equal"); }
public void Decompile(string relPath) { string file = $"{ShaderDirectory}/{relPath}"; // Arrange. // Act. var bytecode = File.ReadAllBytes(file + ".o"); var shader = ShaderReader.ReadShader(bytecode); var hlslWriter = new HlslWriter(shader); string decompiledHlsl = ""; using (var stream = new MemoryStream()) { hlslWriter.Write(stream); stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { decompiledHlsl = reader.ReadToEnd(); } } File.WriteAllText($"{file}.d.hlsl", decompiledHlsl); // Assert. }
public void AsmMatchesFxc(string relPath) { string file = $"{ShaderDirectory}/{relPath}"; // Arrange. var asmFileText = string.Join(Environment.NewLine, File.ReadAllLines(file + ".asm").Select(x => x.Trim())); asmFileText = TestUtils.NormalizeAssembly(asmFileText); // Act. var bytecode = File.ReadAllBytes(file + ".o"); var shader = ShaderReader.ReadShader(bytecode); var asmWriter = new AsmWriter(shader); string decompiledAsm = ""; using (var stream = new MemoryStream()) { asmWriter.Write(stream); stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { decompiledAsm = reader.ReadToEnd(); } } var decompiledAsmText = string.Join(Environment.NewLine, decompiledAsm .Split(new[] { Environment.NewLine }, StringSplitOptions.None) .Select(x => x.Trim())); File.WriteAllText($"{file}.d.asm", decompiledAsm); // Assert. Assert.That(decompiledAsmText, Is.EqualTo(asmFileText)); }
public void RecompileShaders(string relPath) { string file = $"{ShaderDirectory}/{relPath}"; // Arrange. var relDir = Path.GetDirectoryName(relPath); Directory.CreateDirectory($"{OutputDir}/{relDir}"); var sourceName = GetSourceNameFromObject($"{ShaderDirectory}/{relPath}.o"); if (ShaderDirectory != OutputDir) { File.Copy($"{ShaderDirectory}/{relDir}/{sourceName}", $"{OutputDir}/{relDir}/{sourceName}", true); } if (ShaderDirectory != OutputDir) { File.Copy($"{ShaderDirectory}/{relPath}.asm", $"{OutputDir}/{relPath}.asm", true); } var asmFileText = string.Join(Environment.NewLine, File.ReadAllLines(file + ".asm").Select(x => x.Trim())); // Act. var binaryFileBytes = File.ReadAllBytes(file + ".o"); var shaderModel = ShaderReader.ReadShader(binaryFileBytes); var hlslWriter = new HlslWriter(shaderModel); string decompiledHLSL = ""; using (var stream = new MemoryStream()) { hlslWriter.Write(stream); stream.Position = 0; using (var reader = new StreamReader(stream, Encoding.UTF8)) { decompiledHLSL = reader.ReadToEnd(); } } File.WriteAllText($"{OutputDir}/{relPath}.d.hlsl", decompiledHLSL); using (var shaderBytecode = ShaderBytecode.FromStream(new MemoryStream(binaryFileBytes))) { var profile = shaderModel.Type == DX9Shader.ShaderType.Pixel ? $"ps_{shaderModel.MajorVersion}_{shaderModel.MinorVersion}" : $"vs_{shaderModel.MajorVersion}_{shaderModel.MinorVersion}"; var compiledShader = ShaderBytecode.Compile(decompiledHLSL, "main", profile); var disassembly = shaderBytecode.Disassemble(); var redisassembly = compiledShader.Bytecode.Disassemble(); File.WriteAllText($"{OutputDir}/{relPath}.d1.asm", disassembly); File.WriteAllText($"{OutputDir}/{relPath}.d2.asm", redisassembly); // Assert. Warn.If(disassembly, Is.EqualTo(redisassembly)); } // Assert. Assert.Pass(); }
public static StateBlob Parse(BytecodeReader reader, BytecodeReader shaderReader) { var result = new StateBlob(); result.TechniqueIndex = shaderReader.ReadUInt32(); result.PassIndex = shaderReader.ReadUInt32(); result.SamplerStateIndex = shaderReader.ReadUInt32(); result.AssignmentIndex = shaderReader.ReadUInt32(); result.BlobType = (StateBlobType)shaderReader.ReadUInt32(); var dataReader = shaderReader.CopyAtCurrentPosition(); var blobSize = shaderReader.ReadUInt32(); var paddedSize = blobSize + (blobSize % 4 == 0 ? 0 : 4 - blobSize % 4); //Seak ahead var data = shaderReader.ReadBytes((int)paddedSize); if (result.BlobType == StateBlobType.Shader) { result.Shader = ShaderReader.ReadShader(data); } else if (result.BlobType == StateBlobType.Variable) { result.VariableName = dataReader.TryReadString(); } else if (result.BlobType == StateBlobType.IndexShader) { var _blobSize = dataReader.ReadUInt32(); var variableSize = dataReader.ReadUInt32(); result.VariableName = dataReader.ReadString(); if (variableSize > (result.VariableName.Length + 1)) { var paddingCount = variableSize - (result.VariableName.Length + 1); var padding = dataReader.ReadBytes((int)paddingCount); } result.Shader = result.Shader = ShaderModel.Parse(dataReader); } return(result); }
private ShaderInfo DisassembleShader() { return(info = ShaderReader.DisassembleShader(ShaderByteCode, out dissassembly)); }
public void TestEffect9Constants() { var bytecode = ShaderBytecode.Compile(@" struct T { float f; int i; int2 j; float4 v; }; T t : register(vs, c5) : register(ps, c5) = { 1.0f, 2, int2(2, 3), float4(4, 5, 6, 7) }; struct M { column_major float2x3 f; float g; } m : register(vs, c80) : register(ps, c80); struct U { row_major float2x3 f; T t[2]; float g; }; U u[3] : register(vs, c10) : register(ps, c10); float4 VS(float4 p : POSITION) : POSITION { // some random code which uses those constants // so they won't be optimized away. return p * 2 * t.i * u[2].g * m.g * u[0].t[1].v; } technique Tq { pass p0 { VertexShader = compile vs_2_0 VS(); } } " , "fx_2_0").Bytecode; var shaderModel = ShaderReader.ReadShader(bytecode); var constants = from blob in shaderModel.EffectChunk.StateBlobLookup.Values where blob.BlobType == DX9Shader.FX9.StateBlobType.Shader from constdecl in blob.Shader.ConstantTable.ConstantDeclarations select constdecl; // constant "t" var t = constants.First(c => c.Name == "t"); Assert.AreEqual(5, t.RegisterIndex); var t_j = t.GetRegisterTypeByOffset(2); Assert.AreEqual(7, t_j.RegisterIndex); Assert.AreEqual(ParameterClass.Vector, t_j.Type.ParameterClass); Assert.AreEqual(ParameterType.Int, t_j.Type.ParameterType); var int2Name = t.GetMemberNameByOffset(2); Assert.AreEqual("t.j", int2Name); // constant "m" var m = constants.First(c => c.Name == "m"); Assert.AreEqual(80, m.RegisterIndex); var m_f = m.GetRegisterTypeByOffset(2); Assert.AreEqual(80, m_f.RegisterIndex); Assert.AreEqual(ParameterClass.MatrixColumns, m_f.Type.ParameterClass); Assert.AreEqual("m.f", m.GetMemberNameByOffset(2)); var m_g = m.GetRegisterTypeByOffset(3); Assert.AreEqual(83, m_g.RegisterIndex); Assert.AreEqual("m.g", m.GetMemberNameByOffset(3)); // constant "u" var u = constants.First(c => c.Name == "u"); Assert.AreEqual(10, u.RegisterIndex); var u_2_g = u.GetRegisterTypeByOffset(32); Assert.AreEqual(42, u_2_g.RegisterIndex); Assert.AreEqual(ParameterClass.Scalar, u_2_g.Type.ParameterClass); Assert.AreEqual("u[2].g", u.GetMemberNameByOffset(32)); var u_2_t_1_v = u.GetRegisterTypeByOffset(31); Assert.AreEqual(41, u_2_t_1_v.RegisterIndex); Assert.AreEqual(ParameterClass.Vector, u_2_t_1_v.Type.ParameterClass); Assert.AreEqual(ParameterType.Float, u_2_t_1_v.Type.ParameterType); Assert.AreEqual(1, u_2_t_1_v.Type.Rows); Assert.AreEqual(4, u_2_t_1_v.Type.Columns); Assert.AreEqual("u[2].t[1].v", u.GetMemberNameByOffset(31)); }
public void TestSimpleVertexShaderWithConstants() { var bytecode = ShaderBytecode.Compile(@" struct T { float f; float4 v; }; struct U { row_major float2x3 f; T t[2]; float g; }; U u[3]; float4 HakureiReimuAliceMargatroid; row_major float2x2 m; column_major float2x2 n; sampler Gensokyo[2]; float4 VS(float4 p : POSITION) : POSITION { return p * HakureiReimuAliceMargatroid.w * u[2].t[1].v * m[0].y * n[0].y; } float4 PS(float4 t : TEXCOORD) : COLOR { return tex2D(Gensokyo[0], t.xy) + tex2D(Gensokyo[1], t.zw); } technique Tq { pass p0 { VertexShader = compile vs_3_0 VS(); PixelShader = compile ps_3_0 PS(); } } " , "fx_2_0").Bytecode; var shaderModel = ShaderReader.ReadShader(bytecode); var shaders = from blob in shaderModel.EffectChunk.StateBlobLookup.Values where blob.BlobType == DX9Shader.FX9.StateBlobType.Shader select blob.Shader; string AstDecompile(ShaderModel shader) => new HlslWriter(shader, doAstAnalysis: true).Decompile(); var testVertexShader = shaders.First(s => s.Type == ShaderType.Vertex); var decompiledVertexShader = AstDecompile(testVertexShader); // here we use the `.w` component, to make sure `HakureiReimuAliceMargatroid.w` // is inside the actual decompiled shader too, not just inside constant declaration // (that is, `float4 HakureiReimuAliceMargatroid`). StringAssert.Contains("HakureiReimuAliceMargatroid.w", decompiledVertexShader); // assert that `u[2].t[1].v` appears inside the decompiled source code StringAssert.Contains("u[2].t[1].v", decompiledVertexShader); var testPixelShader = shaders.First(s => s.Type == ShaderType.Pixel); var decompiledPixelShader = AstDecompile(testPixelShader); // assert that sampler array elements `Gensokyo[0]` and `[1]` // appears inside the decompile source code StringAssert.Contains("Gensokyo[0]", decompiledPixelShader); StringAssert.Contains("Gensokyo[1]", decompiledPixelShader); var fromNonAst = HlslWriter.Decompile(bytecode); StringAssert.Contains("HakureiReimuAliceMargatroid.w", fromNonAst); StringAssert.Contains("u[2].t[1].v", fromNonAst); StringAssert.Contains("Gensokyo[0]", fromNonAst); StringAssert.Contains("Gensokyo[1]", fromNonAst); }