Created
April 13, 2026 07:24
-
-
Save wotakuro/a6c207e36fa522feac90786ae8370ffa to your computer and use it in GitHub Desktop.
Unity CBUFFER ANLYZER
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using UnityEngine; | |
| using UnityEditor; | |
| using UnityEngine.Rendering; | |
| using UnityEditor.Rendering; | |
| using System.Collections.Generic; | |
| namespace UTJ.Sample | |
| { | |
| public class ShaderConstantBufferAnalyzer : EditorWindow | |
| { | |
| private Shader targetShader; | |
| private Dictionary<string, Dictionary<int, ShaderData.ConstantInfo>> cbFieldsByIndex; | |
| private Dictionary<string, ShaderData.ConstantBufferInfo> cbInfo; | |
| private ShaderCompilerPlatform targetShaderCompilerPlatform = ShaderCompilerPlatform.D3D; | |
| private BuildTarget buildTarget = BuildTarget.StandaloneWindows64; | |
| private Vector2 scroll; | |
| private string dataStr = ""; | |
| [MenuItem("Tools/Shader CBuffer Analyzer")] | |
| public static void ShowWindow() | |
| { | |
| GetWindow<ShaderConstantBufferAnalyzer>("CBuffer Analyzer"); | |
| } | |
| private void OnGUI() | |
| { | |
| GUILayout.Label("Shader Constant Buffer Analyzer", EditorStyles.boldLabel); | |
| this.targetShader = (Shader)EditorGUILayout.ObjectField("Target Shader", this.targetShader, typeof(Shader), false); | |
| this.targetShaderCompilerPlatform = (ShaderCompilerPlatform)EditorGUILayout.EnumPopup("ShaderCompilerPlatform", this.targetShaderCompilerPlatform); | |
| this.buildTarget = (BuildTarget)EditorGUILayout.EnumPopup("BuildTarget", this.buildTarget); | |
| if (GUILayout.Button("Analyze Constant Buffers") && targetShader != null) | |
| { | |
| AnalyzeShader(targetShader); | |
| } | |
| scroll = EditorGUILayout.BeginScrollView(scroll); | |
| EditorGUILayout.TextArea(dataStr); | |
| EditorGUILayout.EndScrollView(); | |
| } | |
| private void AnalyzeShader(Shader shader) | |
| { | |
| ShaderData shaderData = ShaderUtil.GetShaderData(shader); | |
| if (shaderData == null) | |
| { | |
| Debug.LogError("ShaderDataの取得に失敗しました。"); | |
| return; | |
| } | |
| this.cbFieldsByIndex = new Dictionary<string, Dictionary<int, ShaderData.ConstantInfo>>(); | |
| this.cbInfo = new Dictionary<string, ShaderData.ConstantBufferInfo>(); | |
| // 1. Shaderからキーワード一覧を取得する (Unity 2021.2以降) | |
| LocalKeyword[] keywords = shader.keywordSpace.keywords; | |
| for (int i = 0; i < 1; i++) | |
| { | |
| ShaderData.Subshader subshader = shaderData.GetSubshader(i); | |
| for (int j = 0; j < subshader.PassCount; j++) | |
| { | |
| ShaderData.Pass pass = subshader.GetPass(j); | |
| // 2. キーワードなし(ベース)のバリアントをコンパイル | |
| CompileAndLogCBufferInfo(pass, ShaderType.Vertex, new string[0]); | |
| CompileAndLogCBufferInfo(pass, ShaderType.Fragment, new string[0]); | |
| // 3. 取得したキーワードごとにループしてコンパイル | |
| foreach (LocalKeyword keyword in keywords) | |
| { | |
| // 単一のキーワードを有効にして解析 | |
| CompileAndLogCBufferInfo(pass, ShaderType.Vertex, new string[] { keyword.name }); | |
| CompileAndLogCBufferInfo(pass, ShaderType.Fragment, new string[] { keyword.name }); | |
| } | |
| } | |
| } | |
| dataStr = "========================CBuffer=====================\n"; | |
| foreach (var constantKvs in this.cbFieldsByIndex) | |
| { | |
| var cbInfos = new List<ShaderData.ConstantInfo>(constantKvs.Value.Values); | |
| cbInfos.Sort((a, b) => { return a.Index - b.Index; }); | |
| dataStr += $"===== {constantKvs.Key} ( {this.cbInfo[constantKvs.Key].Size} Byte) ====\n"; | |
| foreach (var field in cbInfos) | |
| { | |
| int size = CalculateShaderConstantSize(field); | |
| dataStr += ($" Index:{field.Index}-{field.Index + size} ({size} Byte), Field Name: {field.Name}, DataType: {field.DataType},ConstantType:{field.ConstantType} , Rows:{field.Rows} , Columns:{field.Columns},StructSize: {field.StructSize} ,ArraySize: {field.ArraySize}\n"); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// 指定されたキーワードの配列でシェーダーバリアントをコンパイルし、CBufferの情報を出力します | |
| /// </summary> | |
| private void CompileAndLogCBufferInfo(ShaderData.Pass pass, ShaderType type, string[] keywords) | |
| { | |
| // ログ出力用のキーワード文字列を生成 | |
| string keywordString = keywords.Length == 0 ? "<No Keywords>" : string.Join(" ", keywords); | |
| // 指定したキーワードでバリアントをコンパイル | |
| ShaderData.VariantCompileInfo variantInfo = pass.CompileVariant( | |
| type, // 解析したいシェーダーステージ(Vertex, Fragment等) | |
| keywords, // キーワードの配列 | |
| targetShaderCompilerPlatform, // ターゲットプラットフォーム | |
| this.buildTarget, // ビルドターゲット | |
| GraphicsTier.Tier3 // グラフィックスタリア | |
| ); | |
| if (!variantInfo.Success) | |
| { | |
| // Shaderの記述によっては、特定のパスでサポートされていないキーワードを渡すと失敗することがあります | |
| return; | |
| } | |
| ShaderData.ConstantBufferInfo[] cbInfos = variantInfo.ConstantBuffers; | |
| // CBufferが存在する場合のみログを出力 | |
| if (cbInfos.Length > 0) | |
| { | |
| foreach (var cb in cbInfos) | |
| { | |
| // Skip global | |
| if (cb.Name.Contains("Globals")) | |
| { | |
| continue; | |
| } | |
| if (!this.cbInfo.TryAdd(cb.Name, cb)) | |
| { | |
| if (cb.Size != this.cbInfo[cb.Name].Size) | |
| { | |
| Debug.LogError("ConstantBufferSize Different " + cb.Name + " " + cb.Size + "-" + this.cbInfo[cb.Name].Size); | |
| } | |
| if (cb.Size > this.cbInfo[cb.Name].Size) | |
| { | |
| this.cbInfo[cb.Name] = cb; | |
| } | |
| } | |
| CollectCBInfo(cb, cb.Fields); | |
| } | |
| } | |
| } | |
| private int CalculateShaderConstantSize(in ShaderData.ConstantInfo info) | |
| { | |
| int arraySize = Mathf.Max(1, info.ArraySize); | |
| int rows = Mathf.Max(1, info.Rows); | |
| int columns = Mathf.Max(1, info.Columns); | |
| int size = info.StructSize; | |
| if (info.StructSize <= 0) | |
| { | |
| switch (info.DataType) | |
| { | |
| case ShaderParamType.Float: | |
| case ShaderParamType.Int: | |
| case ShaderParamType.UInt: | |
| size = 4; | |
| break; | |
| case ShaderParamType.Short: | |
| case ShaderParamType.Half: | |
| size = 2; | |
| break; | |
| case ShaderParamType.Bool: | |
| size = 1; | |
| break; | |
| } | |
| } | |
| return size * arraySize * rows * columns; | |
| } | |
| private void CollectCBInfo(ShaderData.ConstantBufferInfo cbInfo, ShaderData.ConstantInfo[] fieldInfos) | |
| { | |
| if (fieldInfos == null) { return; } | |
| Dictionary<int, ShaderData.ConstantInfo> fieldDict; | |
| if (!this.cbFieldsByIndex.TryGetValue(cbInfo.Name, out fieldDict)) | |
| { | |
| fieldDict = new Dictionary<int, ShaderData.ConstantInfo>(); | |
| this.cbFieldsByIndex.Add(cbInfo.Name, fieldDict); | |
| } | |
| foreach (var info in fieldInfos) | |
| { | |
| if (!fieldDict.TryAdd(info.Index, info)) | |
| { | |
| if (fieldDict[info.Index].Name != info.Name) | |
| { | |
| Debug.LogError("Different Name in " + cbInfo.Name + " " + info.Index + "::" + fieldDict[info.Index].Name + "-" + info.Name); | |
| } | |
| } | |
| // CollectCBInfo(cbInfo, info.StructFields); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment