Skip to content

Instantly share code, notes, and snippets.

@wotakuro
Created November 17, 2025 00:49
Show Gist options
  • Select an option

  • Save wotakuro/fc13d755ba3734b2460602655505de13 to your computer and use it in GitHub Desktop.

Select an option

Save wotakuro/fc13d755ba3734b2460602655505de13 to your computer and use it in GitHub Desktop.
GraphicsStateCollectionをMergeしたり、分割するサンプル
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using UnityEngine.Experimental.Rendering;
using UnityEditor.Playables;
namespace UTJ.Sample
{
public class GraphicsStateCollectionSeparatorUI : EditorWindow
{
[MenuItem("Tools/GraphicsStateCollection/SplitByShader")]
public static void Create()
{
GraphicsStateCollectionSeparatorUI.GetWindow<GraphicsStateCollectionSeparatorUI>();
}
private GraphicsStateCollection target;
private void OnEnable()
{
ObjectField field = new ObjectField();
field.objectType = typeof(GraphicsStateCollection);
field.RegisterValueChangedCallback(OnChangeObject);
Button button = new Button();
button.text = "split by Shader";
button.clicked += this.OnClickButton;
this.rootVisualElement.Add(field);
this.rootVisualElement.Add(button);
}
private void OnClickButton()
{
var originalPath = AssetDatabase.GetAssetPath(target);
var directory = originalPath.Substring(0, originalPath.LastIndexOf('.'));
if (!System.IO.Directory.Exists(directory))
{
System.IO.Directory.CreateDirectory(directory);
}
Debug.Log(directory);
var splitDictionary = GraphicsStateCollectionUtility.SplitByShader(target);
foreach (var kvp in splitDictionary)
{
var shader = kvp.Key;
var savePath = System.IO.Path.Combine(directory, shader.name.Replace('/','_').Replace('|','-')+ ".graphicsstate");
GraphicsStateCollectionUtility.SaveAsAsset(kvp.Value, savePath);
}
}
private void OnChangeObject(ChangeEvent<UnityEngine.Object> changeEvent)
{
this.target = changeEvent.newValue as GraphicsStateCollection;
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UTJ.Sample
{
/// <summary>
/// GraphicsStateCollectionのUtility
/// </summary>
public static class GraphicsStateCollectionUtility
{
/// <summary>
/// GraphicsStateCollectionをShaderで分割を行います
/// </summary>
/// <param name="graphicsStateCollection">分割したGraphicsStateCollection</param>
/// <returns>分割元のデータ</returns>
public static Dictionary<Shader, GraphicsStateCollection> SplitByShader(GraphicsStateCollection graphicsStateCollection)
{
Dictionary<Shader, GraphicsStateCollection> dictionary = new Dictionary<Shader, GraphicsStateCollection>();
// GraphicsStateCollectionがないなら、空のDictionaryを返します
if (!graphicsStateCollection)
{
return dictionary;
}
var variants = new List<GraphicsStateCollection.ShaderVariant>(graphicsStateCollection.variantCount);
var variantStatesBuffer = new List<GraphicsStateCollection.GraphicsState>();
// GraphicsStateCollectionにあるVariantでループを回します
graphicsStateCollection.GetVariants(variants);
foreach (var variant in variants)
{
var shader = variant.shader;
if(!shader)
{
continue;
}
// 分割後のGraphicsStateCollectionを得ます
GraphicsStateCollection splitGraphicsState;
if (!dictionary.TryGetValue(shader, out splitGraphicsState))
{
splitGraphicsState = new GraphicsStateCollection();
splitGraphicsState.graphicsDeviceType = graphicsStateCollection.graphicsDeviceType;
splitGraphicsState.runtimePlatform = graphicsStateCollection.runtimePlatform;
dictionary.Add(shader, splitGraphicsState);
}
// Shaderに対応する Variantを設定します
splitGraphicsState.AddVariant(shader, variant.passId, variant.keywords);
// Variantに対応する
// Pipeline State Objectを設定していきます
variantStatesBuffer.Clear();
graphicsStateCollection.GetGraphicsStatesForVariant(variant, variantStatesBuffer);
foreach (var variantState in variantStatesBuffer)
{
splitGraphicsState.AddGraphicsStateForVariant(shader,
variant.passId, variant.keywords, variantState);
}
}
return dictionary;
}
/// <summary>
/// 複数のGraphicsStateCollectionをマージします
/// </summary>
/// <param name="mergedGraphicsStateCollection">マージ先のGraphicsStateCollectionを指定します</param>
/// <param name="graphicsStateCollections">マージしたいList等を渡します</param>
public static void MergeGraphicsStateCollection(
GraphicsStateCollection mergedGraphicsStateCollection,
IEnumerable<GraphicsStateCollection> graphicsStateCollections)
{
var variantBuffer = new List<GraphicsStateCollection.ShaderVariant>();
var variantStatesBuffer = new List<GraphicsStateCollection.GraphicsState>();
// 渡されたリスト等を順次回します
foreach (var gscObjecgt in graphicsStateCollections)
{
// 無効なGraphicsStateCollectionはスキップします
if (!gscObjecgt)
{
continue;
}
variantBuffer.Clear();
gscObjecgt.GetVariants(variantBuffer);
foreach (var variant in variantBuffer)
{
// マージ後にVariant追加
// (追加成功時にはaddVariantResult にtrueが入ります。既にあって追加されない場合 falseとなります
bool addVariantResult = mergedGraphicsStateCollection.AddVariant(variant.shader, variant.passId, variant.keywords);
variantStatesBuffer.Clear();
gscObjecgt.GetGraphicsStatesForVariant(variant, variantStatesBuffer);
foreach(var variantState in variantStatesBuffer)
{
// マージ後にState追加
// (追加成功時にはaddVariantResult にtrueが入ります。既にあって追加されない場合 falseとなります
bool addStateResult = mergedGraphicsStateCollection.AddGraphicsStateForVariant(variant.shader,variant.passId,variant.keywords, variantState);
}
}
}
}
/// <summary>
/// GraphicsStateCollectionをAssetとして保存します
/// </summary>
/// <param name="graphicsStateCollection">保存したいGraphicsStateCollectionを指定します</param>
/// <param name="path">保存パス</param>
/// <returns>保存に成功するとTrueを返します</returns>
public static bool SaveAsAsset(GraphicsStateCollection graphicsStateCollection, string path)
{
if (!graphicsStateCollection)
{
return false;
}
return graphicsStateCollection.SaveToFile(path);
}
}
}
using UnityEditor;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using UnityEngine.Experimental.Rendering;
using UTJ.Sample;
using UnityEditor.Playables;
public class GraphicsStateMergeTool : EditorWindow
{
/// <summary>
/// マージ先の指定
/// </summary>
private ObjectField baseGraphicsStateCollectionField;
/// <summary>
/// ScrollView
/// </summary>
private ScrollView scrollView;
[MenuItem("Tools/GraphicsStateCollection/MergeTool")]
public static void Create()
{
GraphicsStateMergeTool.GetWindow<GraphicsStateMergeTool>();
}
/// <summary>
/// 初期化時
/// </summary>
private void OnEnable()
{
baseGraphicsStateCollectionField = new ObjectField();
baseGraphicsStateCollectionField.objectType = typeof(GraphicsStateCollection);
this.rootVisualElement.Add(new Label("マージ先GraphicsStateCollection"));
this.rootVisualElement.Add(baseGraphicsStateCollectionField);
var mergeButton = new Button();
mergeButton.text = "以下のものをマージする";
mergeButton.clicked += ExecuteMerge;
this.rootVisualElement.Add(mergeButton);
this.scrollView = new ScrollView();
this.rootVisualElement.Add(scrollView);
var plusButton = new Button();
plusButton.text = "+";
plusButton.clicked += OnclickPlusButton;
this.rootVisualElement.Add(plusButton);
this.OnclickPlusButton();
}
/// <summary>
/// マージの実行
/// </summary>
private void ExecuteMerge()
{
// ScrollView以下に下がっているオブジェクトフィールドを探してきます
var objectFields = this.scrollView.Query<ObjectField>();
var graphicStateCollections = new List<GraphicsStateCollection>();
objectFields.ForEach(field =>
{
var gcs = field.value as GraphicsStateCollection;
if (gcs)
{
graphicStateCollections.Add(gcs);
}
});
var baseGcs =baseGraphicsStateCollectionField.value as GraphicsStateCollection;
GraphicsStateCollectionUtility.MergeGraphicsStateCollection(baseGcs, graphicStateCollections);
}
/// <summary>
/// +ボタンが押された時
/// </summary>
private void OnclickPlusButton()
{
VisualElement visualElement = new VisualElement();
visualElement.style.flexDirection = FlexDirection.Row;
var mergeObject = new ObjectField();
mergeObject.objectType = typeof(GraphicsStateCollection);
var deleteButton = new Button();
deleteButton.text = "削除";
// 削除ボタンは、親ごと削除することで実現します
deleteButton.clicked += ()=>{
deleteButton.parent.parent.Remove(deleteButton.parent);
};
visualElement.Add(mergeObject);
visualElement.Add(deleteButton);
this.scrollView.Add(visualElement);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment