From eb3b7d0b758e841d3e6654a28efd6db1e58d14ee Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Thu, 26 Nov 2015 08:28:01 -0800 Subject: [PATCH] Adding a tool to interactively assemble/disassemble shaders using XNA. --- tools/shader-playground/App.config | 5 + tools/shader-playground/Editor.Designer.cs | 116 +++++++++++++++++ tools/shader-playground/Editor.cs | 110 ++++++++++++++++ tools/shader-playground/Editor.resx | 120 ++++++++++++++++++ tools/shader-playground/Program.cs | 49 +++++++ tools/shader-playground/README.md | 11 ++ .../shader-playground.csproj | 68 ++++++++++ tools/shader-playground/shader-playground.sln | 22 ++++ 8 files changed, 501 insertions(+) create mode 100644 tools/shader-playground/App.config create mode 100644 tools/shader-playground/Editor.Designer.cs create mode 100644 tools/shader-playground/Editor.cs create mode 100644 tools/shader-playground/Editor.resx create mode 100644 tools/shader-playground/Program.cs create mode 100644 tools/shader-playground/README.md create mode 100644 tools/shader-playground/shader-playground.csproj create mode 100644 tools/shader-playground/shader-playground.sln diff --git a/tools/shader-playground/App.config b/tools/shader-playground/App.config new file mode 100644 index 000000000..aef709404 --- /dev/null +++ b/tools/shader-playground/App.config @@ -0,0 +1,5 @@ + + + + + diff --git a/tools/shader-playground/Editor.Designer.cs b/tools/shader-playground/Editor.Designer.cs new file mode 100644 index 000000000..0f369a566 --- /dev/null +++ b/tools/shader-playground/Editor.Designer.cs @@ -0,0 +1,116 @@ +namespace shader_playground { + partial class Editor { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.sourceCodeTextBox = new System.Windows.Forms.TextBox(); + this.outputTextBox = new System.Windows.Forms.TextBox(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.wordsTextBox = new System.Windows.Forms.TextBox(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // sourceCodeTextBox + // + this.sourceCodeTextBox.AcceptsReturn = true; + this.sourceCodeTextBox.AcceptsTab = true; + this.sourceCodeTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.sourceCodeTextBox.Location = new System.Drawing.Point(0, 0); + this.sourceCodeTextBox.Multiline = true; + this.sourceCodeTextBox.Name = "sourceCodeTextBox"; + this.sourceCodeTextBox.Size = new System.Drawing.Size(352, 360); + this.sourceCodeTextBox.TabIndex = 0; + // + // outputTextBox + // + this.outputTextBox.AcceptsReturn = true; + this.outputTextBox.AcceptsTab = true; + this.outputTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.outputTextBox.Location = new System.Drawing.Point(0, 0); + this.outputTextBox.Multiline = true; + this.outputTextBox.Name = "outputTextBox"; + this.outputTextBox.ReadOnly = true; + this.outputTextBox.Size = new System.Drawing.Size(349, 360); + this.outputTextBox.TabIndex = 1; + // + // splitContainer1 + // + this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.splitContainer1.Location = new System.Drawing.Point(12, 12); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.sourceCodeTextBox); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.outputTextBox); + this.splitContainer1.Size = new System.Drawing.Size(705, 360); + this.splitContainer1.SplitterDistance = 352; + this.splitContainer1.TabIndex = 2; + // + // wordsTextBox + // + this.wordsTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.wordsTextBox.Location = new System.Drawing.Point(12, 378); + this.wordsTextBox.Multiline = true; + this.wordsTextBox.Name = "wordsTextBox"; + this.wordsTextBox.ReadOnly = true; + this.wordsTextBox.Size = new System.Drawing.Size(705, 251); + this.wordsTextBox.TabIndex = 4; + // + // Editor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(729, 641); + this.Controls.Add(this.wordsTextBox); + this.Controls.Add(this.splitContainer1); + this.Name = "Editor"; + this.Text = "Shader Playground"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + this.splitContainer1.Panel2.PerformLayout(); + this.splitContainer1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox sourceCodeTextBox; + private System.Windows.Forms.TextBox outputTextBox; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.TextBox wordsTextBox; + } +} + diff --git a/tools/shader-playground/Editor.cs b/tools/shader-playground/Editor.cs new file mode 100644 index 000000000..81e7b3a5b --- /dev/null +++ b/tools/shader-playground/Editor.cs @@ -0,0 +1,110 @@ +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Text; +using System.Windows.Forms; + +namespace shader_playground { + public partial class Editor : Form { + public Editor() { + InitializeComponent(); + + wordsTextBox.Click += WordsTextBox_Click; + + sourceCodeTextBox.TextChanged += SourceCodeTextBox_TextChanged; + sourceCodeTextBox.Text = string.Join( + "\r\n", new string[] { + "xps_3_0", + "dcl_texcoord1 r0", + "dcl_color r1.xy", + "exec", + "alloc colors", + "exece", + "mad oC0, r0, r1.y, c0", + "mul r4.xyz, r1.xyz, c0.xyz", + "+ adds r5.w, r0.xy", + "cnop", + }); + } + + private void WordsTextBox_Click(object sender, EventArgs e) { + wordsTextBox.SelectAll(); + wordsTextBox.Copy(); + } + + void SourceCodeTextBox_TextChanged(object sender, EventArgs e) { + Assemble(sourceCodeTextBox.Text); + } + + class NopIncludeHandler : CompilerIncludeHandler { + public override Stream Open(CompilerIncludeHandlerType includeType, + string filename) { + throw new NotImplementedException(); + } + } + + void Assemble(string shaderSourceCode) { + shaderSourceCode += "\ncnop"; + shaderSourceCode += "\ncnop"; + var preprocessorDefines = new CompilerMacro[2]; + preprocessorDefines[0].Name = "XBOX"; + preprocessorDefines[0].Name = "XBOX360"; + var includeHandler = new NopIncludeHandler(); + var options = CompilerOptions.None; + var compiledShader = ShaderCompiler.AssembleFromSource( + shaderSourceCode, preprocessorDefines, includeHandler, options, + Microsoft.Xna.Framework.TargetPlatform.Xbox360); + + DumpWords(compiledShader.GetShaderCode()); + + var disassembledSourceCode = compiledShader.ErrorsAndWarnings; + disassembledSourceCode = disassembledSourceCode.Replace("\n", "\r\n"); + if (disassembledSourceCode.IndexOf("// PDB hint 00000000-00000000-00000000") == -1) { + outputTextBox.Text = disassembledSourceCode; + return; + } + var prefix = disassembledSourceCode.Substring( + 0, disassembledSourceCode.IndexOf(':')); + disassembledSourceCode = + disassembledSourceCode.Replace(prefix + ": warning X7102: ", ""); + disassembledSourceCode = disassembledSourceCode.Replace( + "// PDB hint 00000000-00000000-00000000\r\n", ""); + var firstLine = disassembledSourceCode.IndexOf("//"); + disassembledSourceCode = disassembledSourceCode.Substring(firstLine); + disassembledSourceCode = disassembledSourceCode.Trim(); + outputTextBox.Text = disassembledSourceCode; + } + + void DumpWords(byte[] shaderCode) { + if (shaderCode == null || shaderCode.Length == 0) { + wordsTextBox.Text = ""; + return; + } + + uint[] swappedCode = new uint[shaderCode.Length / sizeof(uint)]; + Buffer.BlockCopy(shaderCode, 0, swappedCode, 0, shaderCode.Length); + for (int i = 0; i < swappedCode.Length; ++i) { + swappedCode[i] = SwapBytes(swappedCode[i]); + } + var sb = new StringBuilder(); + sb.Append("const uint32_t shader_words[] = {"); + for (int i = 0; i < swappedCode.Length; ++i) { + sb.AppendFormat("0x{0:X8}, ", swappedCode[i]); + } + sb.Append("};"); + wordsTextBox.Text = sb.ToString(); + wordsTextBox.SelectAll(); + } + + uint SwapBytes(uint x) { + return ((x & 0x000000ff) << 24) + + ((x & 0x0000ff00) << 8) + + ((x & 0x00ff0000) >> 8) + + ((x & 0xff000000) >> 24); + } + + } +} diff --git a/tools/shader-playground/Editor.resx b/tools/shader-playground/Editor.resx new file mode 100644 index 000000000..d2320fab8 --- /dev/null +++ b/tools/shader-playground/Editor.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/tools/shader-playground/Program.cs b/tools/shader-playground/Program.cs new file mode 100644 index 000000000..21ee94660 --- /dev/null +++ b/tools/shader-playground/Program.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("shader-playground")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("shader-playground")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("05d27ad4-ef23-4fa1-ad5a-7cfd587fd093")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +namespace shader_playground { + static class Program { + [STAThread] + static void Main() { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Editor()); + } + } +} diff --git a/tools/shader-playground/README.md b/tools/shader-playground/README.md new file mode 100644 index 000000000..1c8fe7e6a --- /dev/null +++ b/tools/shader-playground/README.md @@ -0,0 +1,11 @@ +This requires XNA Game Studio 3.1 to be installed (not just the redist): +http://www.microsoft.com/en-us/download/details.aspx?id=39 + +It's not really compatible with modern VS', but you can open the downloaded +`XNAGS31_setup.exe` with 7zip and run the included `redists.msi` directly. + +If installed correctly you should have this file: +`C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v3.1\References\Windows\x86\Microsoft.Xna.Framework.dll` + +XNA is only compatible with 32-bit x86 .NET, so ensure Visual Studio is set to +target that (not `Any CPU`). diff --git a/tools/shader-playground/shader-playground.csproj b/tools/shader-playground/shader-playground.csproj new file mode 100644 index 000000000..163aa713a --- /dev/null +++ b/tools/shader-playground/shader-playground.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {05D27AD4-EF23-4FA1-AD5A-7CFD587FD093} + WinExe + Properties + shader_playground + shader-playground + v2.0 + 512 + + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + Form + + + Editor.cs + + + + Editor.cs + + + + + + + + + + + \ No newline at end of file diff --git a/tools/shader-playground/shader-playground.sln b/tools/shader-playground/shader-playground.sln new file mode 100644 index 000000000..68c02f975 --- /dev/null +++ b/tools/shader-playground/shader-playground.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shader-playground", "shader-playground.csproj", "{05D27AD4-EF23-4FA1-AD5A-7CFD587FD093}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {05D27AD4-EF23-4FA1-AD5A-7CFD587FD093}.Debug|x86.ActiveCfg = Debug|x86 + {05D27AD4-EF23-4FA1-AD5A-7CFD587FD093}.Debug|x86.Build.0 = Debug|x86 + {05D27AD4-EF23-4FA1-AD5A-7CFD587FD093}.Release|x86.ActiveCfg = Release|x86 + {05D27AD4-EF23-4FA1-AD5A-7CFD587FD093}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal