2025-07-08 14:28:40 -04:00
// Amplify Shader Editor - Visual Shader Editing Tool
// Copyright (c) Amplify Creations, Lda <info@amplify.pt>
using System ;
using UnityEngine ;
using UnityEngine.Rendering ;
using UnityEditor ;
using UnityEditor.PackageManager.Requests ;
using System.Collections.Generic ;
using System.IO ;
using System.Reflection ;
using System.Text.RegularExpressions ;
namespace AmplifyShaderEditor
{
public enum ASEImportFlags
{
None = 0 ,
URP = 1 < < 0 ,
HDRP = 1 < < 1 ,
Both = URP | HDRP
}
public static class AssetDatabaseEX
{
private static System . Type type = null ;
public static System . Type Type { get { return ( type = = null ) ? type = System . Type . GetType ( "UnityEditor.AssetDatabase, UnityEditor" ) : type ; } }
public static void ImportPackageImmediately ( string packagePath )
{
AssetDatabaseEX . Type . InvokeMember ( "ImportPackageImmediately" , BindingFlags . Static | BindingFlags . NonPublic | BindingFlags . InvokeMethod , null , null , new object [ ] { packagePath } ) ;
}
}
public enum ASESRPBaseline
{
ASE_SRP_INVALID = 0 ,
2026-03-14 03:13:10 -04:00
ASE_SRP_10_X = 100000 ,
ASE_SRP_11_X = 110000 ,
ASE_SRP_12_X = 120000 ,
ASE_SRP_13_X = 130000 ,
ASE_SRP_14_X = 140000 ,
ASE_SRP_15_X = 150000 ,
ASE_SRP_16_X = 160000 ,
ASE_SRP_17_0 = 170000 ,
ASE_SRP_17_1 = 170100 ,
ASE_SRP_17_2 = 170200
}
2025-07-08 14:28:40 -04:00
public class ASESRPPackageDesc
{
public ASESRPBaseline baseline = ASESRPBaseline . ASE_SRP_INVALID ;
public string guidURP = string . Empty ;
public string guidHDRP = string . Empty ;
public ASESRPPackageDesc ( ASESRPBaseline baseline , string guidURP , string guidHDRP )
{
this . baseline = baseline ;
this . guidURP = guidURP ;
this . guidHDRP = guidHDRP ;
}
}
[Serializable]
[InitializeOnLoad]
public static class ASEPackageManagerHelper
{
2026-03-14 03:13:10 -04:00
public static readonly string URPPackageId = "com.unity.render-pipelines.universal" ;
public static readonly string HDRPPackageId = "com.unity.render-pipelines.high-definition" ;
2025-07-08 14:28:40 -04:00
2026-03-14 03:13:10 -04:00
private static readonly string NewVersionDetectedFormat = "A new {0} version {1} was detected and new templates are being imported.\nPlease hit the Update button on your ASE canvas to recompile your shader under the newest version." ;
private static readonly string PackageBaseFormat = "ASE_PkgBase_{0}_{1}" ;
private static readonly string PackageCRCFormat = "ASE_PkgCRC_{0}_{1}" ;
2025-07-08 14:28:40 -04:00
2026-03-14 03:13:10 -04:00
private static readonly string SRPKeywordFormat = "ASE_SRP_VERSION {0}" ;
private static readonly string ASEVersionKeywordFormat = "ASE_VERSION {0}" ;
2025-07-08 14:28:40 -04:00
2026-03-14 03:13:10 -04:00
public static readonly Dictionary < int , ASESRPPackageDesc > SRPPackageSupport = new Dictionary < int , ASESRPPackageDesc > ( )
2025-07-08 14:28:40 -04:00
{
2026-03-14 03:13:10 -04:00
{ ( int ) ASESRPBaseline . ASE_SRP_10_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_10_X , "b460b52e6c1feae45b70b7ddc2c45bd6" , "2243c8b4e1ab6914995699133f67ab5a" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_11_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_11_X , "b460b52e6c1feae45b70b7ddc2c45bd6" , "2243c8b4e1ab6914995699133f67ab5a" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_12_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_12_X , "57fcea0ed8b5eb347923c4c21fa31b57" , "9a5e61a8b3421b944863d0946e32da0a" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_13_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_13_X , "57fcea0ed8b5eb347923c4c21fa31b57" , "9a5e61a8b3421b944863d0946e32da0a" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_14_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_14_X , "2e9da72e7e3196146bf7d27450013734" , "89f0b84148d149d4d96b838d7ef60e92" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_15_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_15_X , "0904cdf24ddcd5042b024326476220d5" , "19939ee2cdb76e0489b1b8cd4bed7f3d" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_16_X , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_16_X , "929783250050f8a448821b6ca1f2c578" , "70777e8ce9f3c8d4a8182ca2f965cdb2" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_17_0 , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_17_0 , "fcc4d2eb0af82e546ae75506872cf092" , "ba281a1a00c8ac54c914e0763299f637" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_17_1 , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_17_1 , "cd0a0171c5157b748afe763b89f71211" , "e6fc8948257acee42b666d0bfe1d782c" ) } ,
{ ( int ) ASESRPBaseline . ASE_SRP_17_2 , new ASESRPPackageDesc ( ASESRPBaseline . ASE_SRP_17_2 , "f4990f6ace6142c4bbbf41cdd80b0bd3" , "4b5cb8698f2d9c14fadf8e2383441d37" ) } ,
} ;
2025-07-08 14:28:40 -04:00
private static Shader m_lateShader ;
private static Material m_lateMaterial ;
private static AmplifyShaderFunction m_lateShaderFunction ;
private static ListRequest m_packageListRequest = null ;
private static UnityEditor . PackageManager . PackageInfo m_urpPackageInfo ;
private static UnityEditor . PackageManager . PackageInfo m_hdrpPackageInfo ;
public static bool FoundURPVersion { get { return m_urpPackageInfo ! = null ; } }
public static bool FoundHDRPVersion { get { return m_hdrpPackageInfo ! = null ; } }
private static bool m_lateImport = false ;
private static string m_latePackageToImport ;
private static bool m_requireUpdateList = false ;
private static ASEImportFlags m_importingPackage = ASEImportFlags . None ;
public static bool CheckImporter { get { return m_importingPackage ! = ASEImportFlags . None ; } }
public static bool IsProcessing { get { return m_requireUpdateList & & m_importingPackage = = ASEImportFlags . None ; } }
private static ASESRPBaseline m_currentURPBaseline = ASESRPBaseline . ASE_SRP_INVALID ;
private static ASESRPBaseline m_currentHDRPBaseline = ASESRPBaseline . ASE_SRP_INVALID ;
public static ASESRPBaseline CurrentURPBaseline { get { return m_currentURPBaseline ; } }
public static ASESRPBaseline CurrentHDRPBaseline { get { return m_currentHDRPBaseline ; } }
private static int m_packageURPVersion = - 1 ; // @diogo: starts as missing
private static int m_packageHDRPVersion = - 1 ;
public static int PackageSRPVersion { get { return ( m_packageHDRPVersion > = m_packageURPVersion ) ? m_packageHDRPVersion : m_packageURPVersion ; } }
public static int CurrentSRPVersion { get { return UIUtils . CurrentWindow . MainGraphInstance . IsSRP ? PackageSRPVersion : - 1 ; } }
private static string m_projectName = null ;
private static string ProjectName
{
get
{
if ( string . IsNullOrEmpty ( m_projectName ) )
{
string [ ] s = Application . dataPath . Split ( '/' ) ;
m_projectName = s [ s . Length - 2 ] ;
}
return m_projectName ;
}
}
static ASEPackageManagerHelper ( )
{
RequestInfo ( true ) ;
}
static void WaitForPackageListBeforeUpdating ( )
{
if ( m_packageListRequest . IsCompleted )
{
Update ( ) ;
EditorApplication . update - = WaitForPackageListBeforeUpdating ;
}
}
public static void RequestInfo ( bool updateWhileWaiting = false )
{
if ( ! m_requireUpdateList & & m_importingPackage = = ASEImportFlags . None )
{
m_requireUpdateList = true ;
m_packageListRequest = UnityEditor . PackageManager . Client . List ( true ) ;
if ( updateWhileWaiting )
{
EditorApplication . update + = WaitForPackageListBeforeUpdating ;
}
}
}
static void FailedPackageImport ( string packageName , string errorMessage )
{
FinishImporter ( ) ;
}
static void CancelledPackageImport ( string packageName )
{
FinishImporter ( ) ;
}
static void CompletedPackageImport ( string packageName )
{
FinishImporter ( ) ;
}
public static void CheckLatePackageImport ( )
{
if ( ! Application . isPlaying & & m_lateImport & & ! string . IsNullOrEmpty ( m_latePackageToImport ) )
{
m_lateImport = false ;
StartImporting ( m_latePackageToImport ) ;
m_latePackageToImport = string . Empty ;
}
}
2026-03-14 03:13:10 -04:00
public static bool StartImporting ( string packagePath )
2025-07-08 14:28:40 -04:00
{
if ( ! Preferences . Project . AutoSRP )
{
m_importingPackage = ASEImportFlags . None ;
2026-03-14 03:13:10 -04:00
return false ;
2025-07-08 14:28:40 -04:00
}
if ( Application . isPlaying )
{
if ( ! m_lateImport )
{
m_lateImport = true ;
m_latePackageToImport = packagePath ;
Debug . LogWarning ( "Amplify Shader Editor requires the \"" + packagePath + "\" package to be installed in order to continue. Please exit Play mode to proceed." ) ;
}
2026-03-14 03:13:10 -04:00
return false ;
2025-07-08 14:28:40 -04:00
}
AssetDatabase . importPackageCancelled + = CancelledPackageImport ;
AssetDatabase . importPackageCompleted + = CompletedPackageImport ;
AssetDatabase . importPackageFailed + = FailedPackageImport ;
AssetDatabase . ImportPackage ( packagePath , false ) ;
2026-03-14 03:13:10 -04:00
return true ;
2025-07-08 14:28:40 -04:00
}
public static void FinishImporter ( )
{
m_importingPackage = ASEImportFlags . None ;
AssetDatabase . importPackageCancelled - = CancelledPackageImport ;
AssetDatabase . importPackageCompleted - = CompletedPackageImport ;
AssetDatabase . importPackageFailed - = FailedPackageImport ;
}
public static void SetupLateShader ( Shader shader )
{
if ( shader = = null )
return ;
//If a previous delayed object is pending discard it and register the new one
// So the last selection will be the choice of opening
//This can happen when trying to open an ASE canvas while importing templates or in play mode
if ( m_lateShader ! = null )
{
EditorApplication . delayCall - = LateShaderOpener ;
}
RequestInfo ( ) ;
m_lateShader = shader ;
EditorApplication . delayCall + = LateShaderOpener ;
}
public static void LateShaderOpener ( )
{
Update ( ) ;
if ( IsProcessing )
{
EditorApplication . delayCall + = LateShaderOpener ;
}
else
{
AmplifyShaderEditorWindow . ConvertShaderToASE ( m_lateShader ) ;
m_lateShader = null ;
}
}
public static void SetupLateMaterial ( Material material )
{
if ( material = = null )
return ;
//If a previous delayed object is pending discard it and register the new one
// So the last selection will be the choice of opening
//This can happen when trying to open an ASE canvas while importing templates or in play mode
if ( m_lateMaterial ! = null )
{
EditorApplication . delayCall - = LateMaterialOpener ;
}
RequestInfo ( ) ;
m_lateMaterial = material ;
EditorApplication . delayCall + = LateMaterialOpener ;
}
public static void LateMaterialOpener ( )
{
Update ( ) ;
if ( IsProcessing )
{
EditorApplication . delayCall + = LateMaterialOpener ;
}
else
{
AmplifyShaderEditorWindow . LoadMaterialToASE ( m_lateMaterial ) ;
m_lateMaterial = null ;
}
}
public static void SetupLateShaderFunction ( AmplifyShaderFunction shaderFunction )
{
if ( shaderFunction = = null )
return ;
//If a previous delayed object is pending discard it and register the new one
// So the last selection will be the choice of opening
//This can happen when trying to open an ASE canvas while importing templates or in play mode
if ( m_lateShaderFunction ! = null )
{
EditorApplication . delayCall - = LateShaderFunctionOpener ;
}
RequestInfo ( ) ;
m_lateShaderFunction = shaderFunction ;
EditorApplication . delayCall + = LateShaderFunctionOpener ;
}
public static void LateShaderFunctionOpener ( )
{
Update ( ) ;
if ( IsProcessing )
{
EditorApplication . delayCall + = LateShaderFunctionOpener ;
}
else
{
AmplifyShaderEditorWindow . LoadShaderFunctionToASE ( m_lateShaderFunction , false ) ;
m_lateShaderFunction = null ;
}
}
private static readonly string SemVerPattern = @"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" ;
2026-03-14 03:13:10 -04:00
public static int PackageVersionStringToCode ( string version , out int major , out int minor , out int patch )
2025-07-08 14:28:40 -04:00
{
MatchCollection matches = Regex . Matches ( version , SemVerPattern , RegexOptions . Multiline ) ;
bool validMatch = ( matches . Count > 0 & & matches [ 0 ] . Groups . Count > = 4 ) ;
major = validMatch ? int . Parse ( matches [ 0 ] . Groups [ 1 ] . Value ) : 99 ;
minor = validMatch ? int . Parse ( matches [ 0 ] . Groups [ 2 ] . Value ) : 99 ;
patch = validMatch ? int . Parse ( matches [ 0 ] . Groups [ 3 ] . Value ) : 99 ;
int versionCode ;
versionCode = major * 10000 ;
versionCode + = minor * 100 ;
versionCode + = patch ;
return versionCode ;
}
2026-03-14 03:13:10 -04:00
public static void CodeToPackageVersionElements ( int versionCode , out int major , out int minor , out int patch )
2025-07-08 14:28:40 -04:00
{
major = versionCode / 10000 ;
minor = versionCode / 100 - major * 100 ;
patch = versionCode - ( versionCode / 100 ) * 100 ;
}
2026-03-14 03:13:10 -04:00
public static int PackageVersionElementsToCode ( int major , int minor , int patch )
2025-07-08 14:28:40 -04:00
{
return major * 10000 + minor * 100 + patch ;
}
private static void CheckPackageImport ( ASEImportFlags flag , ASESRPBaseline baseline , string guid , string version )
{
Debug . Assert ( flag = = ASEImportFlags . HDRP | | flag = = ASEImportFlags . URP ) ;
string path = AssetDatabase . GUIDToAssetPath ( guid ) ;
if ( ! string . IsNullOrEmpty ( path ) & & File . Exists ( path ) )
{
uint currentCRC = IOUtils . CRC32 ( File . ReadAllBytes ( path ) ) ;
string srpName = flag . ToString ( ) ;
string packageBaseKey = string . Format ( PackageBaseFormat , srpName , ProjectName ) ;
string packageCRCKey = string . Format ( PackageCRCFormat , srpName , ProjectName ) ;
ASESRPBaseline savedBaseline = ( ASESRPBaseline ) EditorPrefs . GetInt ( packageBaseKey ) ;
uint savedCRC = ( uint ) EditorPrefs . GetInt ( packageCRCKey , 0 ) ;
bool foundNewVersion = ( savedBaseline ! = baseline ) | | ( savedCRC ! = currentCRC ) ;
EditorPrefs . SetInt ( packageBaseKey , ( int ) baseline ) ;
EditorPrefs . SetInt ( packageCRCKey , ( int ) currentCRC ) ;
string testPath0 = string . Empty ;
string testPath1 = string . Empty ;
switch ( flag )
{
case ASEImportFlags . URP :
{
testPath0 = AssetDatabase . GUIDToAssetPath ( TemplatesManager . URPLitGUID ) ;
testPath1 = AssetDatabase . GUIDToAssetPath ( TemplatesManager . URPUnlitGUID ) ;
break ;
}
case ASEImportFlags . HDRP :
{
testPath0 = AssetDatabase . GUIDToAssetPath ( TemplatesManager . HDRPLitGUID ) ;
testPath1 = AssetDatabase . GUIDToAssetPath ( TemplatesManager . HDRPUnlitGUID ) ;
break ;
}
}
if ( ! File . Exists ( testPath0 ) | | ! File . Exists ( testPath1 ) | | foundNewVersion )
{
2026-03-14 03:13:10 -04:00
m_importingPackage | = flag ;
if ( StartImporting ( path ) & & foundNewVersion )
2025-07-08 14:28:40 -04:00
{
2026-03-14 03:13:10 -04:00
Debug . Log ( "[AmplifyShaderEditor] " + string . Format ( NewVersionDetectedFormat , srpName , version ) ) ;
2025-07-08 14:28:40 -04:00
}
}
}
}
public static void Update ( )
{
CheckLatePackageImport ( ) ;
if ( m_requireUpdateList & & m_importingPackage = = ASEImportFlags . None )
{
if ( m_packageListRequest ! = null & & m_packageListRequest . IsCompleted & & m_packageListRequest . Result ! = null )
{
m_requireUpdateList = false ;
foreach ( UnityEditor . PackageManager . PackageInfo pi in m_packageListRequest . Result )
{
int version = PackageVersionStringToCode ( pi . version , out int major , out int minor , out int patch ) ;
2026-03-14 03:13:10 -04:00
int baselineMajor = major ;
int baselineMinor = ( major > = 17 ) ? minor : 0 ; // from 17+ baseline includes minor version
int baseline = PackageVersionElementsToCode ( baselineMajor , baselineMinor , 0 ) ;
2025-07-08 14:28:40 -04:00
ASESRPPackageDesc match ;
2026-03-14 03:13:10 -04:00
if ( pi . name . Equals ( URPPackageId ) & & SRPPackageSupport . TryGetValue ( baseline , out match ) )
2025-07-08 14:28:40 -04:00
{
// Universal Rendering Pipeline
m_currentURPBaseline = match . baseline ;
m_packageURPVersion = version ;
m_urpPackageInfo = pi ;
CheckPackageImport ( ASEImportFlags . URP , match . baseline , match . guidURP , pi . version ) ;
}
2026-03-14 03:13:10 -04:00
else if ( pi . name . Equals ( HDRPPackageId ) & & SRPPackageSupport . TryGetValue ( baseline , out match ) )
2025-07-08 14:28:40 -04:00
{
// High-Definition Rendering Pipeline
m_currentHDRPBaseline = match . baseline ;
m_packageHDRPVersion = version ;
m_hdrpPackageInfo = pi ;
CheckPackageImport ( ASEImportFlags . HDRP , match . baseline , match . guidHDRP , pi . version ) ;
}
}
}
}
}
2026-03-14 03:13:10 -04:00
public static void SetASEVersionInfoOnDataCollector ( ref MasterNodeDataCollector dataCollector )
{
if ( m_requireUpdateList )
{
Update ( ) ;
}
dataCollector . AddToDirectives ( string . Format ( ASEVersionKeywordFormat , VersionInfo . FullNumber ) , - 1 , AdditionalLineType . Define ) ;
}
2025-07-08 14:28:40 -04:00
public static void SetSRPInfoOnDataCollector ( ref MasterNodeDataCollector dataCollector )
{
if ( m_requireUpdateList )
{
Update ( ) ;
}
if ( dataCollector . CurrentSRPType = = TemplateSRPType . HDRP )
{
dataCollector . AddToDirectives ( string . Format ( SRPKeywordFormat , m_packageHDRPVersion ) , - 1 , AdditionalLineType . Define ) ;
}
else if ( dataCollector . CurrentSRPType = = TemplateSRPType . URP )
{
dataCollector . AddToDirectives ( string . Format ( SRPKeywordFormat , m_packageURPVersion ) , - 1 , AdditionalLineType . Define ) ;
}
}
}
}