Skip to content

Instantly share code, notes, and snippets.

@wotakuro
Last active October 6, 2023 08:21
Show Gist options
  • Save wotakuro/bc04b8b488f92cd57a46b05011fd5a7b to your computer and use it in GitHub Desktop.
Save wotakuro/bc04b8b488f92cd57a46b05011fd5a7b to your computer and use it in GitHub Desktop.
[Unity]ShaderVariantのStripを調べる君
/**
* ビルドをしたりすると描画がおかしくなってしまうと言う事が起きているので、
* ビルド時のShaderVariant周りをログに書き出すデバッグ機能を作りました
*
* これは、IPreprocessShaders(Stripping scriptable shader variants)でログ書き出しのみを行う事で実現しています。
* https://blog.unity.com/engine-platform/stripping-scriptable-shader-variants
*
* デフォルト挙動では、プロジェクト直下に「ShaderBuildLog」というフォルダを作成し、
* ビルドのタイムスタンプごとにフォルダを作成するようにしています
*
* 実行順が一番早いIPreprocessShadersで情報を 「xxxx_first」というログに書き出し、
* 実行順が一番遅いIPreprocessShadersで情報を 「xxxx_last」というログに書き出しています。
* これによって、URPパッケージ内などにも含まれているIPreprocessShadersで弾かれてしまっているのか…
* それともそれより以前の問題なのかの切り分けに役立ちます。
*
* また「[任意]」 と書いている所を書き換えて良い感じに利用してください
*
*/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.Rendering;
using UnityEditor.Build;
using UnityEngine.Rendering;
using System.IO;
using System.Text;
using UnityEditor.Build.Reporting;
using UnityEditor;
using Unity.Rendering.Universal;
namespace UTJ.Sample
{
/// <summary>
/// ShaderVariantのログを実際に書き出す処理
/// </summary>
public class ReserchForShaderStrip
{
// [任意] 保存フォルダを変えたい場合はコチラ
private static readonly string kTargetDir = "ShaderBuildLog";
private StringBuilder stringBuilder = new StringBuilder(1024);
private string suffix;
private static List<BuiltinShaderDefine> s_allDefines;
private static List<BuiltinShaderDefine> allBuiltinDefines
{
get
{
if(s_allDefines == null)
{
s_allDefines = new List<BuiltinShaderDefine>();
foreach(var e in System.Enum.GetValues(typeof(BuiltinShaderDefine)))
{
s_allDefines.Add( (BuiltinShaderDefine)e);
}
}
return s_allDefines;
}
}
private static DateTime timestamp = DateTime.Now;
public static void ResetTimestamp()
{
timestamp = DateTime.Now;
}
public ReserchForShaderStrip(string s)
{
this.suffix = s;
}
private static bool IsLogTargetShader(Shader s)
{
if (s == null)
{
return false;
}
// [任意] 特定のシェーダーだけ調べたい場合はコチラ
/* // 例:
if ( !s.name.StartsWith("Universal/"))
{
return false;
}
*/
return true;
}
public void ProcessLog(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> datas)
{
if (!IsLogTargetShader(shader))
{
return;
}
string dir = Path.Combine(kTargetDir, timestampDir + "_" + EditorUserBuildSettings.activeBuildTarget.ToString() );
if(!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
string file = Path.Combine(dir, GetFileName(shader,this.suffix));
var shaderData = ShaderUtil.GetShaderData(shader);
int passCount = 0;
if (snippet.pass.SubshaderIndex < shaderData.SubshaderCount) {
var subShaderData = shaderData.GetSubshader((int)snippet.pass.SubshaderIndex);
if (subShaderData != null)
{
passCount = subShaderData.PassCount;
}
}
stringBuilder.Clear();
stringBuilder.Append("=================================================\n");
stringBuilder.Append("- Shader:").Append(shader.name).Append("\n");
stringBuilder.Append("- ShaderType:").Append(snippet.shaderType.ToString()).Append("\n");
stringBuilder.Append("- SubShaderIndex:").Append(snippet.pass.SubshaderIndex).Append("/count:").Append(shaderData.SubshaderCount).Append("\n");
stringBuilder.Append("- PassIndex:").Append(snippet.pass.PassIndex).Append("/count:").Append(passCount).Append("\n");
stringBuilder.Append("- PassName:").Append(snippet.passName).Append("\n");
stringBuilder.Append("- PassType:").Append(snippet.passType.ToString()).Append("\n");
stringBuilder.Append("- ShaderCompilerDataCount:").Append(datas.Count).Append("\n");
int idx = 0;
foreach(var data in datas)
{
var keywordSet = data.shaderKeywordSet.GetShaderKeywords();
var platformKeywordSet = data.platformKeywordSet;
var shaderRequirement = data.shaderRequirements.ToString();
var buildTarget = data.buildTarget.ToString();
var graphicsTier = data.graphicsTier.ToString();
stringBuilder.Append("\n--ShaderCompilerDataIndex:").Append(idx).Append("\n");
stringBuilder.Append("--keyword:");
ExecuteKeywordSet(stringBuilder, keywordSet);
stringBuilder.Append("\n");
stringBuilder.Append("--platformKeywordSet:");
ExecutePlatformKeywordSet(stringBuilder, platformKeywordSet);
stringBuilder.Append("\n");
stringBuilder.Append("--shaderRequirement:").Append(shaderRequirement).Append("\n");
stringBuilder.Append("--buildTarget:").Append(buildTarget).Append("\n");
stringBuilder.Append("--graphicsTier:").Append(graphicsTier).Append("\n");
++idx;
}
stringBuilder.Append("\n\n");
File.AppendAllText(file,stringBuilder.ToString());
}
private void ExecuteKeywordSet(StringBuilder sb,ShaderKeyword[] keywordSet)
{
if(keywordSet == null || keywordSet.Length == 0)
{
sb.Append("<no keyword>");
return;
}
foreach(var keyword in keywordSet)
{
sb.Append(' ').Append(keyword.name);
}
}
private void ExecutePlatformKeywordSet(StringBuilder sb, PlatformKeywordSet platformKeywordSet)
{
var defines = allBuiltinDefines;
foreach (var def in defines) {
if( platformKeywordSet.IsEnabled(def))
{
sb.Append(def).Append(" ");
}
}
}
private string GetFileName(Shader shader,string s)
{
return shader.name.Replace('/', '_') + s +".log";
}
private static string timestampDir
{
get
{
return timestamp.ToString("yyyyMMdd_HHmmss");
}
}
}
/// <summary>
/// 他の IPreprocessShaders が処理される前の状況をログ書き出しします
/// </summary>
public class ReserchForShaderStripLoggerFirst : IPreprocessShaders
{
public int callbackOrder => int.MinValue;
private ReserchForShaderStrip reserchForShaderStrip;
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
if(reserchForShaderStrip == null)
{
reserchForShaderStrip = new ReserchForShaderStrip("__first");
}
reserchForShaderStrip.ProcessLog(shader, snippet, data);
}
}
/// <summary>
/// 他の IPreprocessShaders が処理される後の状況をログ書き出しします
/// </summary>
public class ReserchForShaderStripLoggerLast : IPreprocessShaders
{
public int callbackOrder => int.MaxValue;
private ReserchForShaderStrip reserchForShaderStrip;
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
if (reserchForShaderStrip == null)
{
reserchForShaderStrip = new ReserchForShaderStrip("__last");
}
reserchForShaderStrip.ProcessLog(shader, snippet, data);
}
}
/// <summary>
/// Buildをフックして、フォルダ名に利用するタイムスタンプをリセットします。
/// </summary>
class CustomBuildProcessor : IPreprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(BuildReport report)
{
ReserchForShaderStrip.ResetTimestamp();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment