summaryrefslogtreecommitdiffstats
path: root/Editor/shader_inliner_v2.cs
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2025-03-28 19:35:57 -0700
committeryum <yum.food.vr@gmail.com>2025-03-28 19:35:57 -0700
commit5f84c37a4e95503f28540780c3257f8689cccef9 (patch)
treeeadb8f2d49da8d4793337c95be19be0149f243e2 /Editor/shader_inliner_v2.cs
parente19819cc514aad2781ce5fd9168eca536ee75457 (diff)
add shader inliner
Diffstat (limited to 'Editor/shader_inliner_v2.cs')
-rw-r--r--Editor/shader_inliner_v2.cs192
1 files changed, 192 insertions, 0 deletions
diff --git a/Editor/shader_inliner_v2.cs b/Editor/shader_inliner_v2.cs
new file mode 100644
index 0000000..7b66c6a
--- /dev/null
+++ b/Editor/shader_inliner_v2.cs
@@ -0,0 +1,192 @@
+// !! AI ARTIFACT !!
+// This code was originally generated by Claude 3.5 Sonnet.
+// I wanted to write this tooling like I want a fucking hole in the head so I
+// kindly asked Claude to write it for me. It's shitty and poorly designed, but
+// it works well enough for my purposes.
+// It has been slightly tweaked by me, and validated on *this* codebase. It is
+// provided with no warranty.
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+using System.Linq;
+
+public class ShaderInlinerV2 : EditorWindow
+{
+ private string inputShaderPath;
+ private string outputShaderPath;
+ private int maxOutputLines = 10000000; // 10 million lines limit
+ private int currentLineCount = 0;
+
+ [MenuItem("Tools/yum_food/Shader Inliner (v2)")]
+ public static void ShowWindow()
+ {
+ GetWindow<ShaderInlinerV2>("Shader Inliner");
+ }
+
+ private void OnGUI()
+ {
+ GUILayout.Label("Shader Inliner", EditorStyles.boldLabel);
+
+ inputShaderPath = EditorGUILayout.TextField("Input Shader Path", inputShaderPath);
+ if (GUILayout.Button("Select Input Shader"))
+ {
+ inputShaderPath = EditorUtility.OpenFilePanel("Select Shader", "", "shader");
+ }
+
+ maxOutputLines = EditorGUILayout.IntField("Max Output Lines", maxOutputLines);
+
+ if (GUILayout.Button("Inline Shader"))
+ {
+ if (string.IsNullOrEmpty(inputShaderPath))
+ {
+ EditorUtility.DisplayDialog("Error", "Please select an input shader.", "OK");
+ return;
+ }
+
+ InlineShader();
+ }
+ }
+
+ private void InlineShader()
+ {
+ currentLineCount = 0;
+ string shaderContent = File.ReadAllText(inputShaderPath);
+ string inlinedShader = ProcessShader(shaderContent, Path.GetDirectoryName(inputShaderPath));
+
+ string fileName = Path.GetFileNameWithoutExtension(inputShaderPath);
+ outputShaderPath = Path.Combine(Path.GetDirectoryName(inputShaderPath), $"{fileName}_inlined.shader");
+ File.WriteAllText(outputShaderPath, inlinedShader);
+
+ AssetDatabase.Refresh();
+ EditorUtility.DisplayDialog("Success",
+ $"Inlined shader saved to:\n{outputShaderPath}\nTotal lines: {currentLineCount}", "OK");
+ }
+
+ private string ProcessShader(string content, string basePath)
+ {
+ // Update shader name
+ content = Regex.Replace(content, @"Shader\s+""(.+?)""", match =>
+ {
+ string shaderName = match.Groups[1].Value;
+ return $"Shader \"{shaderName}_inlined\"";
+ });
+
+ // Count initial lines
+ currentLineCount += content.Split('\n').Length;
+ if (currentLineCount > maxOutputLines)
+ {
+ Debug.LogError($"Maximum line count exceeded: {currentLineCount}");
+ return content;
+ }
+
+ // Process all includes, regardless of whether they're in CGPROGRAM blocks
+ content = ProcessIncludes(content, basePath);
+
+ // Check for mismatched preprocessor macros in the entire shader
+ CheckMismatchedMacros(content);
+
+ return content;
+ }
+
+ private string ProcessIncludes(string content, string basePath)
+ {
+ string pattern = @"#include\s+""(.+?)""";
+ return Regex.Replace(content, pattern, match =>
+ {
+ string includePath = match.Groups[1].Value;
+ string fullPath = Path.Combine(basePath, includePath);
+
+ if (File.Exists(fullPath))
+ {
+ string includeContent = File.ReadAllText(fullPath);
+
+ // Count the lines in the included file
+ int includeLines = includeContent.Split('\n').Length;
+ currentLineCount += includeLines;
+
+ // Check if we've exceeded the line limit
+ if (currentLineCount > maxOutputLines)
+ {
+ Debug.LogError($"Maximum line count exceeded ({currentLineCount} lines) while including: {includePath}");
+ return $"// ERROR: Maximum line count exceeded while including: {includePath}";
+ }
+
+ // Process includes recursively
+ return ProcessIncludes(includeContent, Path.GetDirectoryName(fullPath));
+ }
+ else
+ {
+ Debug.LogWarning($"Include file not found: {fullPath}");
+ return match.Value;
+ }
+ });
+ }
+
+ private void CheckMismatchedMacros(string content)
+ {
+ var stack = new Stack<string>();
+ var lines = content.Split('\n');
+ var macroPattern = @"^\s*#(if|ifdef|ifndef|elif|else|endif|if\s+defined)";
+
+ for (int i = 0; i < lines.Length; i++)
+ {
+ var line = lines[i].Trim();
+ var match = Regex.Match(line, macroPattern);
+
+ if (match.Success)
+ {
+ var directive = match.Groups[1].Value;
+
+ switch (directive)
+ {
+ case "if":
+ case "ifdef":
+ case "ifndef":
+ case "if defined":
+ stack.Push(directive);
+ break;
+ case "elif":
+ if (stack.Count == 0 || (stack.Peek() != "if" && stack.Peek() != "elif"))
+ {
+ Debug.LogError($"Mismatched #elif at line {i + 1}");
+ }
+ else
+ {
+ stack.Pop();
+ stack.Push("elif");
+ }
+ break;
+ case "else":
+ if (stack.Count == 0 || (stack.Peek() != "if" && stack.Peek() != "elif"))
+ {
+ Debug.LogError($"Mismatched #else at line {i + 1}");
+ }
+ else
+ {
+ stack.Pop();
+ stack.Push("else");
+ }
+ break;
+ case "endif":
+ if (stack.Count == 0)
+ {
+ Debug.LogError($"Mismatched #endif at line {i + 1}");
+ }
+ else
+ {
+ stack.Pop();
+ }
+ break;
+ }
+ }
+ }
+
+ if (stack.Count > 0)
+ {
+ Debug.LogError($"Unclosed preprocessor directives: {string.Join(", ", stack)}");
+ }
+ }
+}
+