Humanoid animation avatar for mixamo animation support

This commit is contained in:
ThomasCharester committed 2026-10-25 01:37:11 +03:00
1 parent 1de0e93ae0
commit c8bb38ae14
3902 files changed
+76220 -1232642

No files matched your search

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 31ce049756c541d48e0b6e2f8e3b6d45
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,37 +0,0 @@
using System.Runtime.CompilerServices;
// Unity.Burst
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.OptimizeFor))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.FloatMode))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.FloatPrecision))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.BurstCompileAttribute))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.BurstCompiler))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.BurstCompilerOptions))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.BurstExecutionEnvironment))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.BurstRuntime))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.IFunctionPointer))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.FunctionPointer<>))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.NoAliasAttribute))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.SharedStatic<>))]
// Unity.Burst.CompilerServices
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.CompilerServices.Aliasing))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.CompilerServices.AssumeRangeAttribute))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.CompilerServices.Constant))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.CompilerServices.Hint))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.CompilerServices.IgnoreWarningAttribute))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.CompilerServices.SkipLocalsInitAttribute))]
// Unity.Burst.Intrinsics
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v64x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v64x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v64x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v128x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v128x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v128x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v64))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v128))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.v256))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.Common))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.X86))]
[assembly: TypeForwardedTo(typeof(global::Unity.Burst.Intrinsics.Arm))]
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 3a7e9b14d5c84f2d9e1b6f8a0c4d7e2b
@@ -1,4 +0,0 @@
{
"name": "Unity.Burst",
"allowUnsafeCode": true
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 2665a8d13d1b3f18800f46e256720795
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
{
"name": "com.unity.burst",
"displayName": "Burst",
"version": "2.0.0",
"type": "shim",
"description": "Burst is a compiler that translates from IL/.NET bytecode to highly optimized native code using LLVM.",
"_fingerprint": "c3ed5b778d5629115f36f7c7fd174bfefbb658e4"
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5f23a75ebd8dc4ad0a7515c06ffbe647
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,46 +0,0 @@
# Changelog
## [2.1.0] - 2026-01-23
- Bug fix for the GetAssemblyPath breaking the naming of assembly node in test runner
## [2.0.5] - 2023-06-28
- Fixing bug for InstanceID as struct changes
## [2.0.4] - 2023-06-21
- Added support for InstanceID as struct changes
## [2.0.3] - 2022-05-31
- Added unique test ID for repeated tests
- Added ContainKey and ContainValue constaints for dictionaries
- Fixed unexpected null exception when using EmptyConstraint with a null object
## [2.0.2] - 2021-10-19
- STAR reviews updates.
## [2.0.1] - 2021-09-23
- Minumum unity version lowered to 2019.4
## [2.0.0] - 2021-06-23
- Upgrade to dotnet 4.0
## [1.0.6] - 2020-11-30
- isExplicitlyReferenced set to 0 (case 1296162)
## [1.0.5] - 2020-11-04
- Removed pdb files
## [1.0.4] - 2020-11-03
- Added the portable-pdb (DSTR-37)
## [1.0.3] - 2020-10-30
- Fixed being able to load mdb or portable-pdb symbolsbug (DSTR-37)
- Minimum unity version updated (case 1279253)
## [1.0.2] - 2019-12-04
- Added missed metafiles
## [0.0.1] - 2019-02-21
### This is the first release of *Unity Package com.unity.ext.nunit*.
- Migrated the custom version of nunit from inside of unity.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: f49bbe06ffa5ae24abe32abdab430c24
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
# Custom Nunit manual
## Package Summary
A custom version of NUnit used by Unity Test Framework. Based on NUnit version 3.5 and works with all platforms, il2cpp and Mono AOT.
See the [NUnit documentation](https://docs.nunit.org/) for more information.
@@ -1,5 +0,0 @@
Custom NUnit copyright © 2026 Unity Technologies
Licensed under the Unity Package Distribution License (see https://unity3d.com/legal/licenses/Unity_Package_Distribution_License ).
Unless expressly provided otherwise, the software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: f91a00d2dca52b843b2d50ccf750737d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,10 +0,0 @@
# Custom Nunit build to work with Unity
This version of nunit works with all platforms, il2cpp and Mono AOT.
For Nunit Documentation:
https://github.com/nunit/docs/wiki/NUnit-Documentation
This source code for this packages lives at https://github.com/Unity-Technologies/nunit
This custom Nunit is based on Nunit version 3.5, and it is precompiled for .NET 4.0. The uncompiled public version lives here https://github.com/Unity-Technologies/nunit.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5da62a0c1c5218c4aa16b74546a7822d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,28 +0,0 @@
This package contains third-party software components governed by the license(s) indicated below:
---------
Component Name: Nunit 3.5
\
License Type: "MIT"
\
Copyright © 2018 Charlie Poole, Rob Prouse
\
https://nunit.org/
\
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: bfcb3ba499f7d452b92d9a9cc3378788
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a962d501f44484c37814dbad5ed605e9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 27328db3447b143dcb24e8c854d86028
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: e59094b434ebb46e6844f02ec9c4dc42
@@ -1,15 +0,0 @@
{
"displayName": "Custom NUnit",
"name": "com.unity.ext.nunit",
"version": "2.1.0",
"description": "A custom version of NUnit used by Unity Test Framework. Based on NUnit version 3.5 and works with all platforms, il2cpp and Mono AOT.",
"keywords": [
"nunit",
"unittest",
"test"
],
"category": "Libraries",
"dependencies": {},
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.ext.nunit@2.0/manual/index.html",
"_fingerprint": "508b95ff9afebba749610c6bac0176e4fbc0ba6e"
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 8143d3a8390f2c64685e3bc272bd9e90
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ddf83594a37842246b402cff6fdd9a70
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,6 +0,0 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.GraphAuthoring.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.VisualEffectGraph.Editor")]
[assembly: InternalsVisibleTo("Unity.ShaderGraph.Editor")]
[assembly: InternalsVisibleTo("Unity.ShaderGraph.Editor.Tests")]
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 3fe291b9ff7fd434b904d818f37abe5f
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 830e1307cddc88648901839befbee3b8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,42 +0,0 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using UnityEngine;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
// A functional data object used by the provider system (ie. See IShaderObject).
// Definitions should be read-only and immutable. They are used as a template for
// for populating mutable State, which can then be used for UX and the like.
internal interface IDefinition
{
bool IsValid { get; }
string Name { get; }
IEnumerable<string> Namespace => ImmutableArray<string>.Empty;
IReadOnlyCollection<IStrongHint> Hints => ImmutableArray<IStrongHint>.Empty;
}
// Abstracts how or where a Definition comes from, allowing them to be used
// in a context free manner. A provider can be mutable and is stored alongside state,
// and prevents the need from serializing a Definition directly.
// Within tools that utilize the provider system, they should expect a Definition to change
// when modifications to the Provider are made.
internal interface IProvider
{
string ProviderKey { get; }
bool IsValid { get; }
GUID AssetID => default;
string Version => Hints.Version.kUnversioned;
string Group => null;
int GroupPriority => 0;
void Reload() { }
IProvider Clone();
}
internal interface IProvider<T> : IProvider where T : IDefinition
{
T Definition { get; }
bool IProvider.IsValid => Definition?.IsValid ?? false;
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: fbadd59d01fc4d34b86733575b8f8edc
@@ -1,306 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
internal class ProviderLibrary
{
internal static event Action<IReadOnlyList<GUID>> OnProvidersReloaded;
internal static void NotifyProvidersReloaded(IReadOnlyList<GUID> modifiedAssetIDs)
=> OnProvidersReloaded?.Invoke(modifiedAssetIDs);
static ProviderLibrary s_instance;
internal static bool IsInitialized => s_instance != null;
internal static bool TryGetInstance(out ProviderLibrary instance)
{
if (AssetDatabase.IsAssetImportWorkerProcess())
{
instance = null;
return false;
}
if (s_instance is null)
{
s_instance = new ProviderLibrary();
ProviderTypeCache.PopulateLibrary(s_instance);
ShaderReflectionAssetPostProcessor.PopulateFromFiles(s_instance);
}
instance = s_instance;
return true;
}
// Note: The provider key + version concept is being used heavily here, but is never exposed to the public.
Dictionary<(string key, string version), IProvider> m_providers = new();
Dictionary<(string key, string version), List<IProvider>> m_conflictedProviders = new();
Dictionary<string, SortedSet<string>> m_providersByVersion = new();
Dictionary<GUID, HashSet<(string key, string version)>> m_providersByAsset = new();
Dictionary<string, SortedSet<(int priority, (string key, string version) vk)>> m_providersByGroup = new();
IComparer<string> m_versionComparer = null;
IComparer<(int priority, (string key, string version) vk)> m_priorityComparer = null;
static int CompareVersion(string a, string b)
{
if (a == Hints.Version.kUnversioned && b == Hints.Version.kUnversioned) return 0;
if (a == Hints.Version.kUnversioned) return 1;
if (b == Hints.Version.kUnversioned) return -1;
bool ap = Version.TryParse(a, out var pa);
bool bp = Version.TryParse(b, out var pb);
return -((ap && bp) ? pa.CompareTo(pb) : string.Compare(a, b, StringComparison.Ordinal));
}
static int ComparePriority((int priority, (string key, string version) vk) a, (int priority, (string key, string version) vk) b)
{
var cmpPriority = -a.priority.CompareTo(b.priority);
return cmpPriority != 0 ? cmpPriority : a.vk.key.CompareTo(b.vk.key);
}
internal IEnumerable<IProvider<T>> MainProviders<T>() where T : IDefinition
{
foreach (var group in m_providersByGroup.Values)
{
foreach (var gke in group)
{
if (m_providers[gke.vk] is IProvider<T> typedProvider)
{
yield return typedProvider;
break;
}
}
}
// Only emit the latest version iff it is ungrouped. We don't fall back to earlier ungrouped versions.
foreach (var pkv in m_providersByVersion)
{
string version = null;
foreach (var v in pkv.Value) { version = v; break; } // .First()
var provider = m_providers[(pkv.Key, version)];
if (provider is IProvider<T> typedProvider && provider.Group == null)
yield return typedProvider;
}
}
internal IEnumerable<IProvider<T>> ProvidersByGroup<T>(string groupKey) where T : IDefinition
{
if (m_providersByGroup.TryGetValue(groupKey, out var group))
{
foreach (var gke in group)
{
var provider = m_providers[gke.vk];
if (provider is IProvider<T> typedProvider)
yield return typedProvider;
}
}
}
internal IEnumerable<IProvider<T>> ProvidersByVersion<T>(string key) where T : IDefinition
{
if (m_providersByVersion.TryGetValue(key, out var entries))
foreach (var version in entries)
{
var provider = m_providers[(key, version)];
if (provider is IProvider<T> typedProvider)
yield return typedProvider;
}
}
internal void ClearByAssetID(GUID assetID)
{
if (m_providersByAsset.Remove(assetID, out var affected))
foreach (var vk in affected)
RemoveProvider(m_providers[vk]);
List<IProvider> conflictsToRemove = new();
foreach (var conflicts in m_conflictedProviders.Values)
foreach (var conflict in conflicts)
if (conflict.AssetID == assetID)
conflictsToRemove.Add(conflict);
foreach (var conflict in conflictsToRemove)
RemoveProvider(conflict);
}
internal IEnumerable<GUID> GetTrackedAssetIDs() => m_providersByAsset.Keys;
internal bool TryAdd(IProvider provider) // TODO(SVFXG-918): Improve feedback and logging for conflicts.
{
// provider is malformed.
if (provider == null || string.IsNullOrEmpty(provider.ProviderKey) || string.IsNullOrEmpty(provider.Version))
{
return false;
}
var vk = (provider.ProviderKey, provider.Version);
// Key is already conflicted, so we can just add this to the conflicted set.
if (m_conflictedProviders.TryGetValue(vk, out var byConflict))
{
byConflict.Add(provider);
return false;
}
// An unconflicted key already exists, so now this new one and the original are in conflict.
if (!m_providers.TryAdd(vk, provider))
{
m_conflictedProviders.TryAdd(vk, new());
m_conflictedProviders[vk].Add(provider);
m_conflictedProviders[vk].Add(m_providers[vk]);
RemoveProvider(m_providers[vk]);
return false;
}
// Looks good, update bookkeeping.
// Lookup by Version.
m_providersByVersion.TryAdd(provider.ProviderKey, new(m_versionComparer ??= Comparer<string>.Create(CompareVersion)));
m_providersByVersion[provider.ProviderKey].Add(provider.Version);
// Lookup by Asset ID.
if (provider.AssetID != default)
{
m_providersByAsset.TryAdd(provider.AssetID, new());
m_providersByAsset[provider.AssetID].Add(vk);
}
// Lookup by Group Key.
if (provider.Group != null)
{
m_providersByGroup.TryAdd(provider.Group, new(m_priorityComparer ??= Comparer<(int, (string, string))>.Create(ComparePriority)));
var group = m_providersByGroup[provider.Group];
// We only want the latest version of a provider in a group, so we need to check if one is currently in the group first.
var currentFound = false;
var currentVersion = Hints.Version.kUnversioned;
var currentPriority = 0;
foreach (var gke in group)
if (gke.vk.key == provider.ProviderKey)
{
currentFound = true;
currentVersion = gke.vk.version;
currentPriority = gke.priority;
break;
}
if (!currentFound)
{
group.Add((provider.GroupPriority, vk));
}
else if (CompareVersion(provider.Version, currentVersion) > 0)
{
group.Remove((currentPriority, (provider.ProviderKey, currentVersion)));
group.Add((provider.GroupPriority, vk));
}
// else: the version we're adding is actually older than current, so we should do nothing.
}
return true;
}
internal bool TryGet<T>(string key, string version, out IProvider<T> provider) where T : IDefinition
{
provider = null;
if (m_providers.TryGetValue((key, version), out var untypedProvider))
{
if (untypedProvider is IProvider<T> typedProvider)
{
provider = typedProvider;
return true;
}
}
return false;
}
internal bool TryGet<T>(string key, out IProvider<T> provider) where T : IDefinition
{
provider = null;
if (m_providersByVersion.TryGetValue(key, out var byVersion))
{
foreach(var version in byVersion)
{
if (m_providers[(key, version)] is IProvider<T> typedProvider)
{
provider = typedProvider;
return true;
}
}
}
return false;
}
void RemoveProvider(IProvider provider)
{
var vk = (provider.ProviderKey, provider.Version);
if (m_providers.Remove(vk))
{
if (m_providersByAsset.TryGetValue(provider.AssetID, out var byAsset))
{
byAsset.Remove(vk);
if (byAsset.Count == 0)
m_providersByAsset.Remove(provider.AssetID);
}
if (m_providersByVersion.TryGetValue(provider.ProviderKey, out var byVersion))
{
byVersion.Remove(provider.Version);
if (byVersion.Count == 0)
m_providersByVersion.Remove(provider.ProviderKey);
}
if (provider.Group != null && m_providersByGroup.TryGetValue(provider.Group, out var byGroup))
{
byGroup.Remove((provider.GroupPriority, vk));
if (byVersion.Count > 0)
{
foreach(var lkg in byVersion)
{
if (m_providers.TryGetValue((provider.ProviderKey, lkg), out var nextBestProvider))
{
if (nextBestProvider.Group == provider.Group)
{
byGroup.Add((nextBestProvider.GroupPriority, (nextBestProvider.ProviderKey, nextBestProvider.Version)));
break;
}
}
}
}
if (byGroup.Count == 0)
{
m_providersByGroup.Remove(provider.Group);
}
}
}
else if (m_conflictedProviders.ContainsKey(vk))
{
// If we remove a conflicted provider, find its index.
for(int i = 0; i < m_conflictedProviders[vk].Count; ++i)
{
var conflict = m_conflictedProviders[vk][i];
if (conflict == provider || conflict.AssetID == provider.AssetID)
{
// remove it.
m_conflictedProviders[vk].RemoveAt(i);
// if there's only one left it is now uncontested and can be added to the library.
if (m_conflictedProviders[vk].Count == 1)
{
var unconflicted = m_conflictedProviders[vk][0];
m_conflictedProviders.Remove(vk);
TryAdd(unconflicted);
// Conflict Resolved.
}
// Conflict still remains.
break;
}
}
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: ee17435b74f70024fb9de92c559409d3
@@ -1,36 +0,0 @@
using System.Collections.Generic;
using UnityEditor;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
// Interprets domain-agnostic IDefinition hint data into domain-specific properties.
// Subclasses read hints directly from the IDefinition via HintUtils.GetHint<T>().
abstract class StrongHeader<T> where T : IDefinition
{
internal bool isValid { get; private set; }
List<(string message, MessageType severity)> m_msgs;
internal IEnumerable<(string message, MessageType severity)> Messages => m_msgs;
protected void AddMessage(string message, MessageType severity = MessageType.Warning)
=> m_msgs.Add((message, severity));
// Runs once per Process call, whenever the ShaderObject or provider is updated.
abstract protected void OnProcess(T obj, IProvider provider);
internal void Process(T obj, IProvider provider)
{
isValid = obj != null && obj.IsValid && provider != null && provider.IsValid;
if (!isValid)
return;
m_msgs = new();
foreach (var hint in ((IDefinition)obj).Hints)
if (!string.IsNullOrWhiteSpace(hint.Message))
m_msgs.Add((($"{hint.Key}: {hint.Message}"), hint.MessageSeverity));
OnProcess(obj, provider);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 05e93de33ef62d64ab09aff3cc9f5d1c
@@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEditor;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
// TODO(SVFXG-914): Some improvements and simplification can now be made here.
internal interface IStrongHint
{
string Key { get; }
object Value { get; }
string Message { get; }
MessageType MessageSeverity => MessageType.Warning;
bool IsResolved { get; }
bool AlwaysProcess => false;
bool AllowDisqualifiedSynonyms => true;
IReadOnlyCollection<string> Synonyms => null;
IReadOnlyCollection<string> Conflicts => null;
void Initialize(bool found, string rawValue, IDefinition obj, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints) { }
}
internal interface IStrongHint<T> : IStrongHint where T : IDefinition
{
void Initialize(bool found, string rawValue, T obj, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints);
void IStrongHint.Initialize(bool found, string rawValue, IDefinition obj, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (obj is T typed) Initialize(found, rawValue, typed, provider, actualHintKey, rawHints);
}
}
internal readonly struct MessageHint : IStrongHint
{
public string Key { get; }
public object Value => null;
public string Message { get; }
public bool IsResolved => false;
internal MessageHint(string key, string message) { Key = key; Message = message; }
}
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
internal class StrongHintAttribute : Attribute { }
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: d0b0e4814ade9fb41bfb47ca95b277c8
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c5e0642aea2c93748889720a3312fc21
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a4d891bc7986be44b9b3e3c3d194be55
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,143 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;
using Unity.GraphAuthoring.Editor.ProviderSystem.Hints;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
[Serializable]
[ScriptedProvider]
[MovedFrom(false, "UnityEditor.ShaderGraph.ProviderSystem", "Unity.ShaderGraph.Editor")]
internal class ExpressionProvider : IProvider<IShaderFunction>
{
// Expression provider is domain agnostic, but we need some sort of keyword filtering that is domain specific.
internal interface IKeywordFilter
{
bool IsReserved(string name);
}
internal const string kExpressionProviderKey = "unity:shadergraph:scripted:Expression";
public string ProviderKey => kExpressionProviderKey;
public GUID AssetID => default;
private static readonly string[] kNamespace = { "unity_sg_expression" };
internal string Expression => m_expression;
internal string ShaderType => m_type;
public IShaderFunction Definition
{
get
{
if (m_definition == null || !m_definition.IsValid)
Reload();
return m_definition;
}
}
[SerializeField]
string m_name;
[SerializeField]
string m_expression;
[SerializeField]
string m_type;
[SerializeReference]
IKeywordFilter m_keywordFilter;
[NonSerialized]
IShaderFunction m_definition;
internal ExpressionProvider() : this("Expression", "A", "float") { }
internal ExpressionProvider(string name, string expression, string type, IKeywordFilter filter = null)
{
UpdateExpression(name, expression, type, filter);
}
public void Reload()
{
m_definition = ExpressionToShaderFunction(m_name, m_expression, m_type, out _, m_keywordFilter);
}
static IReadOnlyCollection<IStrongHint> BuildFuncHints() =>
new IStrongHint[]
{
new DisplayName("Expression", true),
new ProviderKey(kExpressionProviderKey),
new SearchName("Expression"),
new SearchCategory("Utility"),
new SearchTerms(new[] { "equation", "calculation", "inline", "code" }),
new Precision(true),
};
static IReadOnlyCollection<IStrongHint> BuildParamHints(string displayName) =>
new IStrongHint[]
{
new DisplayName(displayName, true)
};
internal void UpdateExpression(string name, string expression, string type, IKeywordFilter filter = null)
{
m_name = name;
m_type = type;
m_expression = expression;
m_keywordFilter = filter;
Reload();
}
public IProvider Clone()
=> new ExpressionProvider(m_name, m_expression, m_type, m_keywordFilter);
internal static IShaderFunction ExpressionToShaderFunction(string name, string expression, string type, out string finalExpression, IKeywordFilter keywordFilter = null)
{
// don't create fields from comments.
const string kCommentRegex = @"(\/\*.*?\*\/)|(\/\/.*)";
// pattern for getting valid identifiers that aren't functions or members,
// includes expanded european character set.
const string kIdentifierRegex = @"(?<!\.)\b[a-zA-ZŽžÀ-ÿ_][a-zA-ZŽžÀ-ÿ0-9_]*\b(?!\s*[\(])";
List<string> orderedNames = new();
HashSet<string> usedNames = new();
// gather the list of identifiers, deduplicate them, and ignore if they are reserved keywords.
string HandleName(string name)
{
if (!usedNames.Contains(name) && keywordFilter?.IsReserved(name) != true)
{
usedNames.Add(name);
orderedNames.Add(name);
}
return name;
}
// clean out comments.
expression = Regex.Replace(expression, kCommentRegex, "");
// process identifiers.
expression = Regex.Replace(expression, kIdentifierRegex, e => HandleName(e.Value));
finalExpression = expression;
// default initialize if there is no expression left.
if (string.IsNullOrWhiteSpace(expression))
expression = $"({type})0";
IShaderType shaderType = new ShaderType(type);
List<IShaderField> parameters = new();
foreach (var paramName in orderedNames)
parameters.Add(new ShaderField(paramName, true, false, shaderType, BuildParamHints(paramName)));
// trust that the name and type is valid.
return new ShaderFunction(name, kNamespace, parameters, shaderType, $"return {expression};", BuildFuncHints());
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: d946e351cb0d51c47ab87647ccc8002a
@@ -1,69 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
internal class ProviderModelAttribute : Attribute
{
public readonly string ProviderKey;
public ProviderModelAttribute(string providerKey) { ProviderKey = providerKey; }
}
internal class ScriptedProviderAttribute : Attribute { }
internal static class ProviderTypeCache
{
static Dictionary<string, Type> s_providerModels = null;
static List<Type> s_scriptedProviders = null;
private static void BuildCache()
{
if (s_providerModels == null && s_scriptedProviders == null)
{
s_providerModels = new();
s_scriptedProviders = new();
foreach(var type in TypeCache.GetTypesWithAttribute<ProviderModelAttribute>())
{
var providerKey = type.GetCustomAttribute<ProviderModelAttribute>()?.ProviderKey ?? null;
if (providerKey != null)
if (!s_providerModels.TryAdd(providerKey, type))
Debug.LogError($"Attempted to register '{type.Name}' with provider key '{providerKey}', but key is already registered to '{s_providerModels[providerKey]}'.");
}
foreach (var type in TypeCache.GetTypesWithAttribute<ScriptedProviderAttribute>())
s_scriptedProviders.Add(type);
}
}
internal static IEnumerable<Type> GetScriptedProviderTypes()
{
BuildCache();
return s_scriptedProviders;
}
internal static bool TryCreateModel(string key, out object model)
{
BuildCache();
if (s_providerModels.TryGetValue(key, out Type type))
{
model = Activator.CreateInstance(type, true);
return true;
}
model = null;
return false;
}
internal static void PopulateLibrary(ProviderLibrary lib)
{
foreach (var type in GetScriptedProviderTypes())
if (Activator.CreateInstance(type, true) is IProvider provider)
lib.TryAdd(provider);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: df3079b6682f2854d83dbf2c603f771e
@@ -1,92 +0,0 @@
using System;
using UnityEditor;
using UnityEditor.ShaderApiReflection;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
[Serializable]
[MovedFrom(false, "UnityEditor.ShaderGraph.ProviderSystem", "Unity.ShaderGraph.Editor")]
internal class ReflectedFunctionProvider : IProvider<IShaderFunction>
{
public string ProviderKey => m_providerKey;
public string Version => m_version ?? Hints.Version.kUnversioned;
public string Group => m_group;
public int GroupPriority => m_groupPriority;
public GUID AssetID => m_sourceAssetId;
public IShaderFunction Definition
{
get
{
if (m_definition == null || !m_definition.IsValid)
Reload();
return m_definition;
}
}
[SerializeField]
string m_providerKey;
[SerializeField]
string m_version;
[SerializeField]
GUID m_sourceAssetId;
[NonSerialized]
string m_group;
[NonSerialized]
int m_groupPriority;
[NonSerialized]
IShaderFunction m_definition;
internal ReflectedFunctionProvider(GUID assetId, IShaderFunction definition)
{
m_sourceAssetId = assetId;
m_definition = definition;
if (definition.IsValid)
{
m_providerKey = definition.Hints.GetHint<Hints.ProviderKey>()?.Value as string;
m_version = definition.Hints.GetHint<Hints.Version>()?.Value as string;
var groupHint = definition.Hints.GetHint<Hints.GroupKey>();
m_group = groupHint?.Value as string;
m_groupPriority = groupHint?.Priority ?? 0;
}
}
internal ReflectedFunctionProvider(GUID assetId, ReflectedFunction function)
: this(assetId, ShaderReflectionUtils.ToShaderFunction(function))
{
}
public void Reload()
{
if (ShaderReflectionUtils.TryResolve(m_providerKey, m_sourceAssetId, out var function, m_version))
{
m_definition = ShaderReflectionUtils.ToShaderFunction(function, this);
var groupHint = m_definition.Hints.GetHint<Hints.GroupKey>();
m_group = groupHint?.Value as string;
m_groupPriority = groupHint?.Priority ?? 0;
}
else
{
m_definition = null;
m_group = null;
m_groupPriority = 0;
}
}
public IProvider Clone()
// NOTE: Definition is read only, so it's fine for a clone to reference the same object.
=> new ReflectedFunctionProvider(m_sourceAssetId, m_definition);
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: f38ec703e65a04642bf6995418d4ee82
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a8555a979e70c2d4c922c336de6b72f5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,67 +0,0 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
internal class ShaderReflectionAssetPostProcessor : AssetPostprocessor
{
internal static void PopulateFromFiles(ProviderLibrary lib)
{
foreach (var guid in AssetDatabase.FindAssetGUIDs("t: ShaderInclude"))
AnalyzeFile(lib, guid);
}
internal static bool AnalyzeFile(ProviderLibrary lib, GUID assetID)
{
lib.ClearByAssetID(assetID);
var shaderInclude = AssetDatabase.LoadAssetByGUID<ShaderInclude>(assetID);
if (shaderInclude == null)
return false;
var reflection = shaderInclude.Reflection;
if (reflection == null)
return false;
foreach (var func in reflection.ReflectedFunctions)
lib.TryAdd(new ReflectedFunctionProvider(assetID, func));
return true;
}
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload)
{
// This asset post processor isn't meaningful unless the ProviderLibrary is already initialized.
if (!ProviderLibrary.IsInitialized || !ProviderLibrary.TryGetInstance(out var lib))
return;
var modifiedFiles = new List<GUID>();
// ProviderLibrary tracks GUIDs whereas this hook only provides paths;
// for deleted assets, we have no way of determining their GUIDs, so we can just test which are orphaned.
foreach (var assetID in new List<GUID>(lib.GetTrackedAssetIDs()))
{
if (!string.IsNullOrEmpty(AssetDatabase.GUIDToAssetPath(assetID)))
continue;
lib.ClearByAssetID(assetID);
modifiedFiles.Add(assetID);
}
foreach (string path in importedAssets)
{
// We are really looking for ShaderInclude assets.
if (Path.GetExtension(path).ToLower() != ".hlsl")
continue;
var assetID = AssetDatabase.GUIDFromAssetPath(path);
if (AnalyzeFile(lib, assetID))
modifiedFiles.Add(assetID);
}
if (modifiedFiles.Count > 0)
ProviderLibrary.NotifyProvidersReloaded(modifiedFiles);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 4bc1cd11275415e4391e7089a2a670ac
@@ -1,281 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor.ShaderApiReflection;
using UnityEditor;
using GUID = UnityEngine.GUID;
using Unity.GraphAuthoring.Editor.ProviderSystem.Hints;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
// TODO (SVFXG-914): Hints processing and representation improvements planned.
internal static class ShaderReflectionUtils
{
// All registered strong hints and derived lookup maps — built once via TypeCache, reused across all calls.
static IStrongHint[] s_strongHints;
static Dictionary<string, IStrongHint> s_hintsByKey;
static Dictionary<string, List<(string StrongHintKey, int Priority)>> s_synonymMap;
static IStrongHint[] GetStrongHints()
{
if (s_strongHints != null)
return s_strongHints;
var discovered = TypeCache.GetTypesWithAttribute<StrongHintAttribute>();
var strongHints = new List<IStrongHint>();
foreach (var type in discovered)
{
if (!type.IsAbstract && !type.IsInterface && typeof(IStrongHint).IsAssignableFrom(type))
strongHints.Add((IStrongHint)Activator.CreateInstance(type, nonPublic: true));
}
s_strongHints = strongHints.ToArray();
// Build synonym map from hint metadata — cached alongside s_strongHints.
s_hintsByKey = new Dictionary<string, IStrongHint>();
s_synonymMap = new Dictionary<string, List<(string StrongHintKey, int Priority)>>();
foreach (var hint in s_strongHints)
{
s_hintsByKey[hint.Key] = hint;
int priority = 0;
if (!s_synonymMap.TryAdd(hint.Key, new() { (hint.Key, priority) }))
s_synonymMap[hint.Key].Add((hint.Key, priority));
if (!hint.AllowDisqualifiedSynonyms)
continue;
var alternates = new List<string>(DisqualifyKey(hint.Key));
if (hint.Synonyms != null)
foreach (var synonym in hint.Synonyms)
alternates.AddRange(DisqualifyKey(synonym, true));
foreach (var alternate in alternates)
{
priority++;
if (!s_synonymMap.TryAdd(alternate, new() { (hint.Key, priority) }))
s_synonymMap[alternate].Add((hint.Key, priority));
}
}
return s_strongHints;
}
readonly struct FieldHintContext : IProvider<IShaderFunction>
{
readonly IProvider m_real;
public string ProviderKey => m_real?.ProviderKey ?? "";
public GUID AssetID => m_real?.AssetID ?? default;
public IShaderFunction Definition { get; }
public IProvider Clone() => this;
internal FieldHintContext(IProvider real, IShaderFunction contextFunc)
{
m_real = real;
Definition = contextFunc;
}
}
internal static bool TryResolve(string providerKey, GUID assetId, out ReflectedFunction function, string version = null)
{
var source = AssetDatabase.LoadAssetByGUID<ShaderInclude>(assetId)?.Reflection;
function = default;
if (source == null)
return false;
ReflectedFunction firstMatch = default;
bool hasFirstMatch = false;
foreach (var func in source.ReflectedFunctions)
{
string key = ShaderObjectUtils.EvaluateProviderKey(func.Hints, BuildUnresolvedFunction(func));
if (providerKey != key)
continue;
// When no version disambiguation is requested, first match wins.
if (string.IsNullOrEmpty(version))
{
function = func;
return true;
}
// Version disambiguation: prefer the entry whose sg:Version matches exactly.
func.Hints.TryGetValue(Func.kVersion, out var funcVersion);
if (funcVersion == version)
{
function = func;
return true;
}
if (!hasFirstMatch)
{
firstMatch = func;
hasFirstMatch = true;
}
}
if (hasFirstMatch)
{
function = firstMatch;
return true;
}
return false;
}
// Pass 1: build a function with unresolved (hint-free) fields, used as context
// for cross-parameter hint validation and for provider key lookup.
static IShaderFunction BuildUnresolvedFunction(ReflectedFunction refFunc)
{
var returnType = ToShaderType(refFunc.ReturnTypeName);
List<IShaderField> fields = new();
foreach (var param in refFunc.Parameters)
{
var type = ToShaderType(param.TypeName);
bool isInput = param.DirectionFlags == ReflectedParameter.Direction.In || param.DirectionFlags == ReflectedParameter.Direction.InOut;
bool isOutput = param.DirectionFlags == ReflectedParameter.Direction.Out || param.DirectionFlags == ReflectedParameter.Direction.InOut;
fields.Add(new ShaderField(param.Name, isInput, isOutput, type, null));
}
return new ShaderFunction(refFunc.Name, refFunc.EnclosingNamespace, fields, returnType, refFunc.BodyText, null);
}
internal static IShaderType ToShaderType(string typeName)
=> new ShaderType(typeName);
internal static IShaderFunction ToShaderFunction(ReflectedFunction refFunc, IProvider provider = null)
{
var returnType = ToShaderType(refFunc.ReturnTypeName);
// Pass 1: unresolved fields — gives cross-parameter hints (e.g. Linkage)
// a fully populated sibling list to validate against.
var unresolvedFunc = BuildUnresolvedFunction(refFunc);
var fieldContext = new FieldHintContext(provider, unresolvedFunc);
// Pass 2: resolve field hints with the context function available.
List<IShaderField> resolvedFields = new();
foreach (var param in refFunc.Parameters)
{
var type = ToShaderType(param.TypeName);
bool isInput = param.DirectionFlags == ReflectedParameter.Direction.In || param.DirectionFlags == ReflectedParameter.Direction.InOut;
bool isOutput = param.DirectionFlags == ReflectedParameter.Direction.Out || param.DirectionFlags == ReflectedParameter.Direction.InOut;
var tempField = new ShaderField(param.Name, isInput, isOutput, type, null);
var fieldHints = ResolveRawHints(param.Hints, tempField, fieldContext);
resolvedFields.Add(new ShaderField(param.Name, isInput, isOutput, type, fieldHints));
}
// Resolve function hints using the final resolved fields.
var tempFunc = new ShaderFunction(refFunc.Name, refFunc.EnclosingNamespace, resolvedFields, returnType, refFunc.BodyText, null);
var funcHints = ResolveRawHints(refFunc.Hints, tempFunc, provider);
return new ShaderFunction(refFunc.Name, refFunc.EnclosingNamespace, resolvedFields, returnType, refFunc.BodyText, funcHints);
}
// Resolves a raw string hint dict against all registered hints, returning
// a collection of resolved IStrongHint instances. Handles synonym matching,
// conflict detection, and AlwaysProcess fallbacks.
static IReadOnlyCollection<IStrongHint> ResolveRawHints(
IReadOnlyDictionary<string, string> rawHints,
IDefinition obj,
IProvider provider)
{
var hints = GetStrongHints();
var hintsByKey = s_hintsByKey;
var synonymMap = s_synonymMap;
var result = new List<IStrongHint>();
// Match raw hint keys to hint keys via synonym map.
var foundHints = new Dictionary<string, (string Synonym, int Priority)>();
var conflictCases = new Dictionary<string, HashSet<string>>();
var conflictedHints = new HashSet<string>();
foreach (var rawHintKey in rawHints.Keys)
{
if (!synonymMap.TryGetValue(rawHintKey, out var matches))
continue;
foreach (var match in matches)
{
if (!foundHints.TryAdd(match.StrongHintKey, (rawHintKey, match.Priority)))
{
if (match.Priority < foundHints[match.StrongHintKey].Priority)
foundHints[match.StrongHintKey] = (rawHintKey, match.Priority);
}
if (hintsByKey[match.StrongHintKey].Conflicts != null)
foreach (var conflictClass in hintsByKey[match.StrongHintKey].Conflicts)
if (!conflictCases.TryAdd(conflictClass, new HashSet<string>() { match.StrongHintKey }))
conflictCases[conflictClass].Add(match.StrongHintKey);
}
}
// Emit conflict messages and collect conflicted hint keys.
foreach (var caseKV in conflictCases)
{
if (caseKV.Value.Count == 1)
continue;
conflictedHints.UnionWith(caseKV.Value);
StringBuilder sb = new();
bool first = true;
sb.Append($"Conflicting hints of class '{caseKV.Key}' found, ignoring: ");
foreach (var conflictKey in caseKV.Value)
{
if (!first) sb.Append(", ");
sb.Append($"'{conflictKey}'");
first = false;
}
result.Add(new MessageHint(caseKV.Key, sb.ToString()));
}
// Resolve each hint.
foreach (var hint in hints)
{
if (conflictedHints.Contains(hint.Key))
continue;
string synonymUsed = null;
string rawHintValue = null;
bool found;
bool shouldProcess = hint.AlwaysProcess;
if (found = foundHints.TryGetValue(hint.Key, out var rawHintData))
{
shouldProcess = true;
synonymUsed = rawHintData.Synonym;
rawHintValue = rawHints[synonymUsed];
}
if (!shouldProcess)
continue;
var instance = (IStrongHint)Activator.CreateInstance(hint.GetType(), nonPublic: true);
instance.Initialize(found, rawHintValue, obj, provider, synonymUsed, rawHints);
if (instance.IsResolved || instance.Message != null)
result.Add(instance);
}
return result;
}
// eg. "unity:engine:sg:HintKey" => "engine:sg:HintKey", "sg:HintKey", "HintKey"
static IEnumerable<string> DisqualifyKey(string key, bool inclusive = false)
{
if (inclusive)
yield return key;
for (int i = 0; i < key.Length - 1; ++i)
{
if (key[i] == ':')
{
int j;
for (j = i + 1; j < key.Length && key[j] == ':'; ++j);
string candidate = key[j..];
if (!string.IsNullOrEmpty(candidate))
yield return candidate;
}
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 0ec8bccc9e164dc43a2fdc214146b251
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 31edee6589f1955409b2de461b4b2acf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,60 +0,0 @@
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
// TODO(SVFXG-869): ShaderType to provide more robust typing information and improved type handling.
internal struct ShaderType : IShaderType
{
public bool IsValid { get; private set; }
public string Name { get; private set; }
internal ShaderType(string name)
{
IsValid = true;
Name = name;
}
}
internal struct ShaderField : IShaderField
{
public bool IsValid { get; private set; }
public string Name { get; private set; }
public bool IsInput { get; private set; }
public bool IsOutput { get; private set; }
public IShaderType ShaderType { get; private set; }
public IReadOnlyCollection<IStrongHint> Hints { get; private set; }
internal ShaderField(string name, bool isInput, bool isOutput, IShaderType shaderType, IReadOnlyCollection<IStrongHint> hints)
{
IsValid = true;
Name = name;
IsInput = isInput;
IsOutput = isOutput;
ShaderType = shaderType;
Hints = hints ?? ImmutableArray<IStrongHint>.Empty;
}
}
internal struct ShaderFunction : IShaderFunction
{
public bool IsValid { get; private set; }
public string Name { get; private set; }
public IShaderType ReturnType { get; private set; }
public string FunctionBody { get; private set; }
public IEnumerable<string> Namespace { get; private set; }
public IEnumerable<IShaderField> Parameters { get; private set; }
public IReadOnlyCollection<IStrongHint> Hints { get; private set; }
internal ShaderFunction(string name, IEnumerable<string> namespaces, IEnumerable<IShaderField> parameters, IShaderType returnType, string functionBody, IReadOnlyCollection<IStrongHint> hints)
{
IsValid = true;
Name = name;
ReturnType = returnType;
FunctionBody = functionBody;
Parameters = parameters ?? ImmutableArray<IShaderField>.Empty;
Namespace = namespaces ?? ImmutableArray<string>.Empty;
Hints = hints ?? ImmutableArray<IStrongHint>.Empty;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 60eb5edc9e8f1644ab7af39eff49a691
@@ -1,163 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
internal static class HintUtils
{
internal static T GetHint<T>(this IReadOnlyCollection<IStrongHint> hints) where T : class, IStrongHint
{
if (hints == null) return null;
foreach (var h in hints)
if (h.GetType() == typeof(T)) return (T)h;
return null;
}
internal static bool IsNumeric(string typeName)
{
return TryParseTypeInfo(typeName, out _, out _, out _, out _, out _, out _, out _);
}
internal static bool TryParseTypeInfo(
string type,
out string prim,
out bool isScalar,
out bool isVector,
out bool isMatrix,
out int rows,
out int cols,
out int length)
{
prim = null;
isScalar = false;
isVector = false;
isMatrix = false;
rows = -1;
cols = -1;
length = -1;
// Arrays aren't supported yet.
if (type.Contains('['))
return false;
if (type.StartsWith("uint")) prim = "uint";
if (type.StartsWith("int")) prim = "int";
if (type.StartsWith("bool")) prim = "bool";
if (type.StartsWith("half")) prim = "half";
if (type.StartsWith("float")) prim = "float";
if (prim == null)
return false;
if (type == prim)
return isScalar = true;
var remainder = type.Substring(prim.Length);
if (remainder.Length == 1)
{
return isVector = int.TryParse(remainder, out rows) && rows >= 1 && rows <= 4;
}
if (remainder.Length == 3)
{
return isMatrix
= int.TryParse(remainder.Substring(0, 1), out rows) && rows >= 1 && rows <= 4
&& int.TryParse(remainder.Substring(2, 1), out cols) && cols >= 1 && cols <= 4;
}
return false;
}
internal static bool TryCast<T>(string shaderType, float[] values, out T value, T fallback = default)
{
try
{
return Cast<T>(shaderType, values, out value, fallback);
}
catch
{
value = default;
return false;
}
}
private static bool Cast<T>(string shaderType, float[] values, out T value, T fallback = default)
{
object obj = default;
value = fallback;
if (values == null || values.Length == 0)
return false;
float[] v = new float[16];
for (int i = 0; i < values.Length; ++i)
v[i] = values[i];
if (!TryParseTypeInfo(shaderType, out string prim, out bool isScalar, out bool isVector, out bool isMatrix, out int rows, out int cols, out int length))
return false;
if (isScalar || isVector && rows == 1 || isMatrix && rows == 1 && cols == 1)
{
var fv = v[0];
switch (prim)
{
case "bool": obj = fv != 0; break;
case "uint": obj = fv; break;
case "int": obj = fv; break;
case "float":
case "half": obj = fv; break;
default: return false;
}
}
if (isVector)
{
switch (rows)
{
case 2: obj = new Vector2(v[0], v[1]); break;
case 3: obj = new Vector3(v[0], v[1], v[2]); break;
case 4: obj = new Vector4(v[0], v[1], v[2], v[3]); break;
default: return false;
}
}
if (isMatrix)
{
Matrix4x4 m = new();
for (int i = 0; i < cols * rows; ++i)
{
m[i % rows + rows * (i / rows)] = v[i];
}
}
value = (T)obj;
return true;
}
internal static float[] LazyTokenFloat(string arg)
{
var tokens = LazyTokenize(arg);
List<float> asfloat = new();
foreach (var token in tokens)
if (float.TryParse(token, out float r))
asfloat.Add(r);
return asfloat.ToArray();
}
internal static string[] LazyTokenString(string arg)
{
List<string> tokens = new(LazyTokenize(arg));
return tokens.ToArray();
}
private static IEnumerable<string> LazyTokenize(string arg)
{
foreach (var e in arg.Split(','))
{
var result = e.Trim();
if (!string.IsNullOrWhiteSpace(result))
yield return e.Trim();
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: bc1a7e8c79fbc8347b285edc784176af
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d2ef992c77ae08e499bfa4ddc2e2fb5a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,116 +0,0 @@
using System.Collections.Generic;
namespace Unity.GraphAuthoring.Editor.ProviderSystem.Hints
{
internal static class Common
{
internal const string kDisplayName = "sg:DisplayName";
internal const string kTooltip = "sg:Tooltip";
}
[StrongHint]
internal class DisplayName : IStrongHint<IDefinition>
{
public string Key { get; }
public bool AlwaysProcess => true;
public IReadOnlyCollection<string> Synonyms { get; } = new string[] { "Label", "Title" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
readonly string m_fallback;
// Default constructor — used by TypeCache auto-discovery.
internal DisplayName() { Key = Common.kDisplayName; }
internal DisplayName(string name, string fallback = null)
{
Key = name;
m_fallback = fallback;
}
// Direct-value constructor — used by ExpressionProvider.
internal DisplayName(string name, string resolvedValue, bool resolved)
{
Key = name;
Value = resolvedValue;
IsResolved = true;
}
// Standard-key direct-value constructor — key defaults to Common.kDisplayName.
internal DisplayName(string resolvedValue, bool resolved)
{
Key = Common.kDisplayName;
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IDefinition obj, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
Value = found ? rawValue : m_fallback ?? obj.Name;
IsResolved = true;
}
}
[StrongHint]
internal class Tooltip : IStrongHint<IDefinition>
{
public string Key { get; }
public IReadOnlyCollection<string> Synonyms { get; } = new[] { "summary" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Tooltip() { Key = Common.kTooltip; }
internal Tooltip(string name) { Key = name; }
// Direct-value constructor — used by ExpressionProvider.
internal Tooltip(string name, string resolvedValue, bool resolved)
{
Key = name;
Value = resolvedValue;
IsResolved = resolvedValue != null;
}
public void Initialize(bool found, string rawValue, IDefinition obj, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
Value = found ? rawValue : null;
IsResolved = found && rawValue != null;
}
}
internal abstract class Flag : IStrongHint<IDefinition>
{
public string Key { get; }
public IReadOnlyCollection<string> Conflicts { get; }
public IReadOnlyCollection<string> Synonyms { get; }
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Base constructors — used by named subclasses.
internal Flag(string name) { Key = name; }
internal Flag(string name, string conflict) { Key = name; Conflicts = conflict != null ? new[] { conflict } : null; }
internal Flag(string name, string[] conflicts) { Key = name; Conflicts = conflicts; }
internal Flag(string name, string[] conflicts, string[] synonyms) { Key = name; Conflicts = conflicts; Synonyms = synonyms; }
// Resolved-value constructor — for subclasses that need to set IsResolved at construction time.
internal Flag(string name, string[] conflicts, string[] synonyms, bool resolved)
{
Key = name; Conflicts = conflicts; Synonyms = synonyms; IsResolved = resolved;
}
public void Initialize(bool found, string rawValue, IDefinition obj, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
Value = rawValue;
IsResolved = found;
if (found && !string.IsNullOrWhiteSpace(rawValue))
Message = "Expects an empty argument string.";
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: f6dbb282170632844b643899f786605a
@@ -1,461 +0,0 @@
using System.Collections.Generic;
using System.Text;
using UnityEditor;
namespace Unity.GraphAuthoring.Editor.ProviderSystem.Hints
{
internal static class Func
{
internal const string kProviderKey = "sg:ProviderKey";
internal const string kReturnDisplayName = "sg:ReturnDisplayName";
internal const string kSearchTerms = "sg:SearchTerms";
internal const string kSearchName = "sg:SearchName";
internal const string kSearchCategory = "sg:SearchCategory";
internal const string kPrecision = "sg:DynamicPrecision";
internal const string kGroupKey = "sg:GroupKey";
internal const string kReturnTooltip = "sg:ReturnTooltip";
internal const string kDocumentationLink = "sg:HelpURL";
internal const string kUnityManualBaseUrl = "https://docs.unity3d.com/Manual/";
internal const string kPackageDocsBaseUrl = "https://docs.unity3d.com/Packages/";
// Lifecycle and versioning.
internal const string kVersion = "sg:Version";
internal const string kDeprecated = "sg:Deprecated";
internal const string kObsolete = "sg:Obsolete";
// Conflict class shared by Deprecated and Obsolete — a function can be one or the other, not both.
internal const string kLifecycleConflictClass = "sg:Lifecycle";
}
[StrongHint] sealed class ReturnDisplayName : DisplayName
{
internal ReturnDisplayName() : base(Func.kReturnDisplayName, "Out") { }
}
[StrongHint] sealed class ReturnTooltip : Tooltip
{
internal ReturnTooltip() : base(Func.kReturnTooltip) { }
}
[StrongHint] sealed class Precision : Flag
{
// Default constructor — used by TypeCache auto-discovery and HLSL hint processing.
internal Precision() : base(Func.kPrecision, (string[])null, new string[] { "Precision" }) { }
// Direct-value constructor — for use in code when precision support is known at construction time.
internal Precision(bool resolved) : base(Func.kPrecision, null, new string[] { "Precision" }, resolved) { }
}
[StrongHint]
class ProviderKey : IStrongHint<IShaderFunction>
{
public string Key => Func.kProviderKey;
public bool AlwaysProcess => true;
public bool AllowDisqualifiedSynonyms => false;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal ProviderKey() { }
// Direct-value constructor.
internal ProviderKey(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (found && !string.IsNullOrWhiteSpace(rawValue))
{
Value = rawValue;
}
else
{
Value = provider?.ProviderKey ?? ShaderObjectUtils.QualifySignature(func);
if (!found)
Message = $"Expected; but none found for '{func.Name}'.";
}
IsResolved = true;
}
}
[StrongHint]
class SearchName : IStrongHint<IShaderFunction>
{
public string Key => Func.kSearchName;
public bool AlwaysProcess => true;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal SearchName() { }
// Direct-value constructor.
internal SearchName(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
Value = found ? rawValue : ShaderObjectUtils.QualifySignature(func, false, true);
IsResolved = true;
}
}
[StrongHint]
class SearchTerms : IStrongHint<IShaderFunction>
{
public string Key => Func.kSearchTerms;
public bool AlwaysProcess => true;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal SearchTerms() { }
// Direct-value constructor (pre-split array).
internal SearchTerms(string[] resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
Value = found ? HintUtils.LazyTokenString(rawValue) : new string[] { func.Name };
IsResolved = true;
}
}
[StrongHint]
class SearchCategory : IStrongHint<IShaderFunction>
{
public string Key => Func.kSearchCategory;
public bool AlwaysProcess => true;
public IReadOnlyCollection<string> Synonyms { get; } = new[] { "Category" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal SearchCategory() { }
// Direct-value constructor.
internal SearchCategory(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (found)
{
Value = rawValue;
}
else
{
StringBuilder catsb = new();
foreach (var name in func.Namespace)
catsb.Append($"/{name}");
var category = catsb.ToString();
if (!string.IsNullOrWhiteSpace(category))
Value = $"Reflected by Namespace{category}";
else if (provider != null && provider.AssetID != default)
Value = $"Reflected by Path/{AssetDatabase.GUIDToAssetPath(provider.AssetID)}";
else
Value = "Uncategorized";
}
IsResolved = true;
}
}
// Resolves to a fully-constructed URL string. Three raw value forms are accepted:
//
// "https://example.com/custom" — raw URL, passed through unchanged
// "com.unity.shadergraph, MyPage" — package + page, builds package docs URL
// "MyPage" — Unity Manual page name, builds manual URL
//
// Value is always the final URL string, ready for use as-is.
[StrongHint]
class HelpURL : IStrongHint<IShaderFunction>
{
public string Key => Func.kDocumentationLink;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
internal HelpURL() { }
internal HelpURL(string resolvedUrl)
{
Value = resolvedUrl;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
// TODO(SVFXG-919): Handle manual pages and, for unity owned definitions, fallback based on Group/Provider key.
if (!found) return;
if (string.IsNullOrWhiteSpace(rawValue)) { Message = "Expected a URL, a page name, or 'packageName, pageName'."; return; }
// Raw URL — contains a scheme separator.
if (rawValue.Contains("://"))
{
Value = rawValue.Trim();
IsResolved = true;
return;
}
var tokens = HintUtils.LazyTokenString(rawValue);
if (tokens.Length > 0)
{
string page = tokens[0];
UnityEditor.PackageManager.PackageInfo packageInfo = null;
if (tokens.Length == 2) // package name is explicitly provided.
{
var packageName = tokens[1];
packageInfo = UnityEditor.PackageManager.PackageInfo.FindForPackageName(packageName);
packageInfo ??= UnityEditor.PackageManager.PackageInfo.FindForPackageName($"com.unity.{packageName}");
if (packageInfo == null)
{
Message = $"Package named '{packageName}' could not be found.";
return;
}
}
else if (provider.AssetID == default) // provider is scripted, so assume the package based on the assembly.
{
packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssembly(provider.GetType().Assembly);
if (packageInfo == null)
{
Message = $"Could not find package for type '{provider.GetType()}'.";
return;
}
}
else // has an assetID, so assume the package of the asset.
{
var path = AssetDatabase.GUIDToAssetPath(provider.AssetID);
if (path == null)
{
Message = $"Failed to resolve valid path from asset '{provider.AssetID}'.";
return;
}
packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(path);
if (packageInfo == null)
{
Message = $"Could not find package for asset at {path}.";
return;
}
}
var name = packageInfo.name;
var version = packageInfo.version.Substring(0, packageInfo.version.LastIndexOf('.')); // pkg versions are x.y.z; doc pages are x.y.
Value = $"{Func.kPackageDocsBaseUrl}{name}@{version}/manual/{page}.html";
IsResolved = true;
return;
}
Message = $"Expected a URL, a page name, or 'packageName, pageName', but found {tokens.Length} comma-separated tokens.";
}
}
// Version string for a function provider. The raw string is stored in Value; if it parses
// as a System.Version (e.g. "1.2.3"), ParsedVersion is also populated for ordered comparison.
[StrongHint]
class Version : IStrongHint<IShaderFunction>
{
// Reserved version string for providers that don't declare an sg:Version hint.
// IProvider.Version defaults to this value, and Initialize rejects it so a user
// can't declare it explicitly.
internal const string kUnversioned = "<unversioned>";
public string Key => Func.kVersion;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Non-null when Value parses as a dotted numeric version (e.g. "1.2.3").
internal System.Version ParsedVersion { get; private set; }
internal Version() { }
internal Version(string resolvedValue)
{
Value = resolvedValue;
System.Version.TryParse(resolvedValue, out var parsed);
ParsedVersion = parsed;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (string.IsNullOrWhiteSpace(rawValue)) { Message = "Expected a version string."; return; }
if (rawValue == kUnversioned) { Message = $"'{kUnversioned}' is reserved; pick a different version string."; return; }
Value = rawValue;
System.Version.TryParse(rawValue, out var parsed);
ParsedVersion = parsed;
IsResolved = true;
}
// Compares two Version hints. Null is treated as less than any non-null version.
// Uses ParsedVersion for dotted numeric strings; falls back to ordinal string compare.
internal static int Compare(Version a, Version b)
{
if (a?.ParsedVersion != null && b?.ParsedVersion != null)
return a.ParsedVersion.CompareTo(b.ParsedVersion);
return string.Compare(a?.Value as string, b?.Value as string, System.StringComparison.Ordinal);
}
}
// Marks a function as deprecated — still functional, but users should migrate away.
// Value is a human-readable message explaining why and what to use instead.
[StrongHint]
class Deprecated : IStrongHint<IShaderFunction>
{
public string Key => Func.kDeprecated;
public IReadOnlyCollection<string> Conflicts { get; } = new[] { Func.kLifecycleConflictClass };
public object Value { get; private set; }
public string Message { get; private set; }
public MessageType MessageSeverity => MessageType.Warning;
public bool IsResolved { get; private set; }
internal Deprecated() { }
internal Deprecated(string message)
{
Value = message;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
Value = rawValue; // may be null/empty — that's valid (bare flag)
Message = string.IsNullOrWhiteSpace(rawValue)
? $"'{func.Name}' is deprecated."
: $"'{func.Name}' is deprecated: {rawValue}";
IsResolved = true;
}
}
// Marks a function as obsolete — scheduled for removal. Stronger than Deprecated.
// Value is a human-readable message explaining when it will be removed and what replaces it.
[StrongHint]
class Obsolete : IStrongHint<IShaderFunction>
{
public string Key => Func.kObsolete;
public IReadOnlyCollection<string> Conflicts { get; } = new[] { Func.kLifecycleConflictClass };
public object Value { get; private set; }
public string Message { get; private set; }
public MessageType MessageSeverity => MessageType.Error;
public bool IsResolved { get; private set; }
internal Obsolete() { }
internal Obsolete(string message)
{
Value = message;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
Value = rawValue;
Message = string.IsNullOrWhiteSpace(rawValue)
? $"'{func.Name}' is obsolete and will be removed."
: $"'{func.Name}' is obsolete and will be removed: {rawValue}";
IsResolved = true;
}
}
// Groups multiple providers under a shared identity key so callers can discover
// all providers belonging to a conceptual set (e.g. several implementations of
// the same operation). An optional integer priority lets the calling code
// determine a preferred default without the library imposing an ordering.
//
// Raw value forms:
// "MyGroup" — group key only, priority defaults to 0
// "MyGroup, 5" — group key with explicit priority
[StrongHint]
class GroupKey : IStrongHint<IShaderFunction>
{
public string Key => Func.kGroupKey;
public object Value { get; private set; } // group key string
internal int Priority { get; private set; } // 0 if not specified
internal string DisplayName { get; private set; } // null if not declared
internal string Label { get; private set; } // null if not declared
public string Message { get; private set; }
public bool IsResolved { get; private set; }
internal GroupKey() { }
internal GroupKey(string groupKey, int priority = 0, string displayName = null, string label = null)
{
Value = groupKey;
Priority = priority;
DisplayName = displayName;
Label = label;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderFunction func, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (string.IsNullOrWhiteSpace(rawValue)) { Message = "Expected a group key string."; return; }
var tokens = HintUtils.LazyTokenString(rawValue);
if (tokens.Length == 1)
{
Value = tokens[0];
DisplayName = tokens[0];
IsResolved = true;
}
else if (tokens.Length >= 2)
{
if (!int.TryParse(tokens[1], out var priority))
{
Message = $"Expected an integer priority as the second token, but found '{tokens[1]}'.";
return;
}
Value = tokens[0];
DisplayName = tokens[0];
Priority = priority;
if (tokens.Length >= 3) DisplayName = tokens[2];
if (tokens.Length >= 4) Label = tokens[3];
if (tokens.Length > 4)
{
Message = $"Expected 1-4 comma-separated tokens (key[, priority[, displayName[, label]]]), but found {tokens.Length}.";
return;
}
IsResolved = true;
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: f8161e4e684587e468cd9de3de9568d3
@@ -1,490 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace Unity.GraphAuthoring.Editor.ProviderSystem.Hints
{
internal static class Param
{
internal const string kAccessModifier = "AccessModifier";
internal const string kCustomEditor = "CustomEditor";
internal const string kStatic = "sg:Static";
internal const string kLocal = "sg:Local";
internal const string kLiteral = "sg:Literal";
internal const string kColor = "sg:Color";
internal const string kRange = "sg:Range";
internal const string kDropdown = "sg:Dropdown";
internal const string kDefault = "sg:Default";
internal const string kExternal = "sg:External";
internal const string kSetting = "sg:Setting";
internal const string kLinkage = "sg:Linkage";
internal const string kDynamic = "sg:DynamicVector";
internal const string kReferable = "sg:Referable";
internal const string kCustomBinding = "sg:CustomBinding";
internal static class Ref
{
internal const string kUV = "UV";
internal const string kPosition = "Position";
internal const string kNormal = "Normal";
internal const string kBitangent = "Bitangent";
internal const string kTangent = "Tangent";
internal const string kViewDirection = "ViewDirection";
internal const string kScreenPosition = "ScreenPosition";
internal const string kVertexColor = "VertexColor";
}
}
[StrongHint] sealed class Local : Flag
{
internal Local() : base(Param.kLocal, new string[] { Param.kAccessModifier }) { }
}
[StrongHint]
internal class Range : IStrongHint<IShaderField>
{
public string Key => Param.kRange;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kCustomEditor };
public IReadOnlyCollection<string> Synonyms { get; } = new string[] { "Slider" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Range() { }
// Direct-value constructor.
internal Range(float[] resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
if (field.ShaderType.Name != "half" && field.ShaderType.Name != "float")
{
Message = $"Expected floating point scalar, but found '{field.ShaderType.Name}'.";
return;
}
float min = 0;
float max = 1;
if (string.IsNullOrWhiteSpace(rawValue)) { Value = new float[] { min, max }; IsResolved = true; return; }
float[] values = HintUtils.LazyTokenFloat(rawValue);
if (values.Length > 2 || !string.IsNullOrEmpty(rawValue) && values.Length == 0)
{
Message = $"Expected 0, 1, or 2 floating point values, but found '{values.Length}'.";
return;
}
if (values.Length == 1)
{
if (values[0] < 0) { min = values[0]; max = 0; }
else max = values[0];
}
else if (values.Length >= 2)
{
min = Mathf.Min(values);
max = Mathf.Max(values);
}
Value = new float[2] { min, max };
IsResolved = true;
if (min == max)
Message = $"Expected min and max to be different values, but both are '{min}'.";
}
}
[StrongHint]
internal class Dropdown : IStrongHint<IShaderField>
{
public string Key => Param.kDropdown;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kCustomEditor };
public IReadOnlyCollection<string> Synonyms { get; } = new string[] { "Enum" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Dropdown() { }
// Direct-value constructor.
internal Dropdown(string[] options)
{
Value = options;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
switch (field.ShaderType.Name)
{
case "int": case "uint": case "float": case "half": break;
default: Message = $"Expected numeric scalar, but found '{field.ShaderType.Name}'."; return;
}
string[] options = HintUtils.LazyTokenString(rawValue);
if (options.Length == 0) { Message = "Expected at least 1 comma separated option, but found none."; return; }
Value = options;
IsResolved = true;
}
}
[StrongHint]
internal class Color : IStrongHint<IShaderField>
{
public string Key => Param.kColor;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kCustomEditor };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Color() { }
// Direct-value constructor (flag present).
internal Color(bool resolved)
{
IsResolved = resolved;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
switch (field.ShaderType.Name)
{
case "float3": case "float4": case "half3": case "half4":
Value = rawValue; IsResolved = true; break;
default:
Message = $"Expected floating point vector of length 3 or 4, but found '{field.ShaderType.Name}'."; break;
}
}
}
[StrongHint]
internal class Literal : IStrongHint<IShaderField>
{
public string Key => Param.kLiteral;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Literal() { }
// Direct-value constructor.
internal Literal(bool resolved)
{
IsResolved = resolved;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
switch (field.ShaderType.Name)
{
case "float": case "half": case "int": case "uint":
Value = rawValue; IsResolved = true; break;
default:
Message = $"Expected numeric scalar, but found '{field.ShaderType.Name}'."; break;
}
}
}
[StrongHint]
internal class Static : IStrongHint<IShaderField>
{
public string Key => Param.kStatic;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kAccessModifier };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Static() { }
// Direct-value constructor.
internal Static(bool resolved)
{
IsResolved = resolved;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
switch (field.ShaderType.Name)
{
case "float": case "half": case "int": case "uint": case "bool":
Value = rawValue; IsResolved = true; break;
case "float3": case "float4": case "half3": case "half4":
if (rawHints?.ContainsKey(Param.kColor) == true) { Value = rawValue; IsResolved = true; break; }
Message = $"Requires '{Param.kColor}' to support '{field.ShaderType.Name}'."; break;
default:
Message = $"Expected '{Param.kColor}' or scalar, but found '{field.ShaderType.Name}'."; break;
}
}
}
[StrongHint]
internal class External : IStrongHint<IShaderField>
{
public string Key => Param.kExternal;
public IReadOnlyCollection<string> Synonyms { get; } = new string[] { "ExternalNamespace" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal External() { }
// Direct-value constructor.
internal External(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
Value = rawValue;
IsResolved = true;
}
}
[StrongHint]
internal class Default : IStrongHint<IShaderField>
{
public string Key => Param.kDefault;
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Default() { }
// Direct-value constructor.
internal Default(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
// TODO(SVFXG-868): Multiple formats to support; pass along raw value for now.
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
Value = rawValue;
IsResolved = true;
}
}
[StrongHint]
internal class Dynamic : IStrongHint<IShaderField>
{
public string Key => Param.kDynamic;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kCustomEditor };
public IReadOnlyCollection<string> Synonyms { get; } = new string[] { "Dynamic" };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Dynamic() { }
// Direct-value constructor.
internal Dynamic(bool resolved)
{
IsResolved = resolved;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
switch (field.ShaderType.Name)
{
case "float": case "float2": case "float3": case "float4":
case "half": case "half2": case "half3": case "half4":
Value = rawValue; IsResolved = true; break;
default:
Message = $"Expected floating point vector or scalar, but found {field.ShaderType.Name}."; break;
}
}
}
[StrongHint]
internal class Linkage : IStrongHint<IShaderField>
{
public string Key => Param.kLinkage;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kAccessModifier };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Linkage() { }
// Direct-value constructor.
internal Linkage(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
if (field.ShaderType.Name != "bool") { Message = $"Expected type bool, but found '{field.ShaderType.Name}'."; return; }
if (rawValue == field.Name) { Message = "Cannot check linkage state against itself."; return; }
// Validate the target parameter exists on the enclosing function.
// The provider here is a FieldHintContext whose Definition is the already-built
// unresolved function, so this is safe and non-recursive.
var funcProvider = provider as IProvider<IShaderFunction>;
if (funcProvider?.Definition != null)
{
bool hasMatch = false;
foreach (var param in funcProvider.Definition.Parameters)
if (param.Name == rawValue) { hasMatch = true; break; }
if (!hasMatch) { Message = $"Could not find expected parameter '{rawValue}'."; return; }
}
Value = rawValue;
IsResolved = true;
}
}
[StrongHint]
internal class Referable : IStrongHint<IShaderField>
{
public string Key => Param.kReferable;
static HashSet<string> s_commonReferables;
private static HashSet<string> GetCommonReferables()
{
if (s_commonReferables == null)
{
s_commonReferables = new HashSet<string>() {
Param.Ref.kUV,
Param.Ref.kPosition,
Param.Ref.kTangent,
Param.Ref.kNormal,
Param.Ref.kBitangent,
Param.Ref.kViewDirection,
Param.Ref.kVertexColor,
Param.Ref.kScreenPosition,
};
}
return s_commonReferables;
}
public IReadOnlyCollection<string> Synonyms => GetCommonReferables();
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kCustomEditor };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal Referable() { }
// Direct-value constructor.
internal Referable(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
object value = Key == actualHintKey ? rawValue : actualHintKey;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
if (value == null) { Message = "Could not resolve a referable type."; return; }
if (!GetCommonReferables().Contains((string)value)) { Message = $"'{value}' is not a supported referable key."; return; }
switch ((string)value)
{
case Param.Ref.kUV:
if (field.ShaderType.Name != "half2" && field.ShaderType.Name != "float2")
{ Message = $"'{value}' expects floating point vector of length 2, but found '{field.ShaderType.Name}'."; return; }
break;
case Param.Ref.kVertexColor:
case Param.Ref.kScreenPosition:
if (field.ShaderType.Name != "half4" && field.ShaderType.Name != "float4")
{ Message = $"'{value}' expects floating point vector of length 4, but found '{field.ShaderType.Name}'."; return; }
break;
default:
if (field.ShaderType.Name != "half3" && field.ShaderType.Name != "float3")
{ Message = $"'{value}' expects floating point vector of length 3, but found '{field.ShaderType.Name}'."; return; }
break;
}
Value = value;
IsResolved = true;
}
}
[StrongHint]
internal class CustomBinding : IStrongHint<IShaderField>
{
public string Key => Param.kCustomBinding;
public IReadOnlyCollection<string> Conflicts { get; } = new string[] { Param.kCustomEditor };
public object Value { get; private set; }
public string Message { get; private set; }
public bool IsResolved { get; private set; }
// Default constructor — used by TypeCache auto-discovery.
internal CustomBinding() { }
// Direct-value constructor.
internal CustomBinding(string resolvedValue)
{
Value = resolvedValue;
IsResolved = true;
}
public void Initialize(bool found, string rawValue, IShaderField field, IProvider provider, string actualHintKey, IReadOnlyDictionary<string, string> rawHints)
{
if (!found) return;
if (!field.IsInput) { Message = "Expected input parameter."; return; }
if (string.IsNullOrEmpty(rawValue)) { Message = "Invalid value."; return; }
Value = rawValue;
IsResolved = true;
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 45a7e22cad240154d8b9f9de72768fa8
@@ -1,20 +0,0 @@
using System.Collections.Generic;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
internal interface IShaderType : IDefinition { }
internal interface IShaderField : IDefinition
{
bool IsInput { get; }
bool IsOutput { get; }
IShaderType ShaderType { get; }
}
internal interface IShaderFunction : IDefinition
{
IShaderType ReturnType { get; }
string FunctionBody { get; }
IEnumerable<IShaderField> Parameters { get; }
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: efe6c69ae14849341a7ea16ccb2f88ae
@@ -1,176 +0,0 @@
using System.Collections.Generic;
using System.Text;
namespace Unity.GraphAuthoring.Editor.ProviderSystem
{
internal static class ShaderObjectUtils
{
internal static string QualifyName(IDefinition obj, string separator = "::", bool includeName = true)
{
if (obj == null || !obj.IsValid)
return null;
StringBuilder builder = new();
if (obj.Namespace != null)
foreach (var name in obj.Namespace)
builder.Append($"{name}{separator}");
builder.Append(obj.Name);
return builder.ToString();
}
internal static string QualifySignature(IShaderFunction obj, bool includeNamespace = true, bool forDisplay = false)
{
if (obj == null || !obj.IsValid)
return null;
StringBuilder builder = new();
builder.Append(includeNamespace ? QualifyName(obj) : obj.Name);
builder.Append(forDisplay ? " (" : "(");
bool first = true;
if (obj.Parameters != null)
{
foreach (var param in obj.Parameters)
{
if (!first) builder.Append(forDisplay ? ", " : ",");
first = false;
builder.Append(QualifyName(param.ShaderType));
}
}
builder.Append(")");
return builder.ToString();
}
// Searches the resolved IStrongHint collection for the provider key hint.
internal static string EvaluateProviderKey(IShaderFunction obj, string fromHint = Hints.Func.kProviderKey)
{
if (obj?.IsValid != true)
return null;
if (obj.Hints != null)
foreach (var h in obj.Hints)
if (h.Key == fromHint && h.IsResolved)
return h.Value as string ?? QualifySignature(obj);
return QualifySignature(obj);
}
// Fast provider-key lookup from a raw string dict (used at construction time before hints are resolved).
internal static string EvaluateProviderKey(IReadOnlyDictionary<string, string> rawHints, IShaderFunction contextObj, string fromHint = Hints.Func.kProviderKey)
{
if (rawHints != null && rawHints.TryGetValue(fromHint, out var result) && !string.IsNullOrWhiteSpace(result))
return result;
return QualifySignature(contextObj);
}
private static string DeclareField(IShaderField a)
{
(string typeName, string arraySpec) SplitTypeName(string typeName)
{
int i = typeName.IndexOf('[');
return i == -1 ? (typeName, "") : (typeName.Substring(0, i - 1), typeName.Substring(i));
}
var t = SplitTypeName(a.ShaderType.Name);
string access = a.IsInput && a.IsOutput ? "inout " : a.IsOutput ? "out " : "";
return $"{access}{t.typeName} {a.Name}{t.arraySpec}";
}
private static string HintValueToString(object value)
{
return value switch
{
string s => s,
string[] arr => string.Join(", ", arr),
float[] arr => string.Join(", ", arr),
_ => value?.ToString() ?? ""
};
}
private static string GenerateHints(string closure, IReadOnlyCollection<IStrongHint> hints, string name = null)
{
StringBuilder sb = new();
bool any = false;
foreach (var hint in hints)
{
if (!hint.IsResolved) continue;
if (!any)
{
sb.AppendLine(name != null ? $"/// <{closure} name = \"{name}\">" : $"/// <{closure}>");
any = true;
}
sb.AppendLine($"///\t <{hint.Key}>{HintValueToString(hint.Value)}</{hint.Key}>");
}
if (any) sb.AppendLine($"/// </{closure}>");
return sb.ToString();
}
internal static string GenerateCall(IShaderFunction func, string argList)
{
StringBuilder call = new();
string funcName = func.Name;
foreach (var name in func.Namespace)
call.Append($"{name}::");
call.Append(funcName);
call.Append("(");
call.Append(argList);
call.Append(");");
return call.ToString();
}
internal static string GenerateCode(IShaderFunction func, bool generateHints = true, bool export = true, bool generateNamespace = true)
{
StringBuilder sb = new();
int indent = 0;
void Ln(string text = "") { for (int i = 0; i < indent; i++) sb.Append(" "); sb.AppendLine(text); }
if (generateNamespace)
foreach (var name in func.Namespace)
sb.Append($"namespace {name} {{");
sb.AppendLine();
indent++;
if (generateHints)
{
if (func.Hints.Count > 0)
sb.Append(GenerateHints("funchints", func.Hints));
foreach (var param in func.Parameters)
if (param.Hints.Count > 0)
sb.Append(GenerateHints("paramhints", param.Hints, param.Name));
}
string funcName = func.Name;
string typeName = func.ReturnType.Name;
sb.Append($"{(export ? "UNITY_EXPORT_REFLECTION" : "")} {typeName} {funcName}(");
bool first = true;
foreach (var param in func.Parameters)
{
if (!first) sb.Append(", ");
first = false;
sb.Append(DeclareField(param));
}
Ln(")");
Ln("{");
indent++;
Ln(func.FunctionBody);
indent--;
Ln("}");
indent--;
if (generateNamespace)
foreach (var name in func.Namespace)
sb.Append($"}} /*{name}*/");
return sb.ToString();
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 3cd2d0e59bfe3c848951929a7a8a9d91
@@ -1,17 +0,0 @@
{
"name": "Unity.GraphAuthoring.Editor",
"references": [
"Unity.GraphToolkit.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6758f1c983038e7449f699022bddab75
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: fdb2a8c7af9a1d442b363fda8ffd9acc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2cbe8b74022c4af41b7c58598b80da16
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,553 +0,0 @@
using System.Collections.Generic;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using Unity.GraphAuthoring.Editor.ProviderSystem;
using Unity.GraphAuthoring.Editor.ProviderSystem.Hints;
using Version = Unity.GraphAuthoring.Editor.ProviderSystem.Hints.Version;
namespace Unity.GraphAuthoring.Editor.Tests
{
// Shared base for ProviderLibrary-focused fixtures: common GUIDs and the
// ShaderFunction factories used by the versioned-coexistence and
// conflict-resolution suites.
abstract class ProviderLibraryTestFixtureBase
{
protected static readonly GUID GuidA = new GUID("aaaaaaaa000000000000000000000000");
protected static readonly GUID GuidB = new GUID("bbbbbbbb000000000000000000000000");
protected static readonly GUID GuidC = new GUID("cccccccc000000000000000000000000");
// ReflectedFunctionProvider reads its ProviderKey from the sg:ProviderKey hint —
// tests must declare it explicitly or TryAdd's validation will reject the provider.
protected static ShaderFunction FuncWithVersion(string name, string version) =>
new ShaderFunction(name, null, null, new ShaderType("void"), "",
new IStrongHint[] { new ProviderKey($"{name}()"), new Version(version) });
protected static ShaderFunction FuncNoVersion(string name) =>
new ShaderFunction(name, null, null, new ShaderType("void"), "",
new IStrongHint[] { new ProviderKey($"{name}()") });
}
// Minimal IDefinition for StubProvider — ProvidersByVersion<T> filters by IProvider<T>,
// so StubProvider needs to be IProvider<something>; the stub definition satisfies that
// without bringing in IShaderFunction's machinery.
class StubDefinition : IDefinition
{
public bool IsValid => true;
public string Name { get; }
internal StubDefinition(string name) { Name = name; }
}
// Minimal IProvider<IDefinition> stub — no asset loading required. The registration tests
// pass real GUIDs and exercise asset-flow code paths (m_providersByAsset tracking,
// ClearByAssetID, conflict reporting against asset paths).
class StubProvider : IProvider<IDefinition>
{
public string ProviderKey { get; }
public GUID AssetID { get; }
public IDefinition Definition { get; }
public IProvider Clone() => this;
internal StubProvider(string key, GUID assetId)
{
ProviderKey = key;
AssetID = assetId;
Definition = new StubDefinition(key);
}
}
// Test helpers for ProviderLibrary fixtures.
static class ProviderLibraryTestExtensions
{
internal static List<IProvider<T>> ToList<T>(this IEnumerable<IProvider<T>> source)
where T : IDefinition
{
var list = new List<IProvider<T>>();
foreach (var item in source) list.Add(item);
return list;
}
}
[TestFixture]
class ProviderLibraryTests : ProviderLibraryTestFixtureBase
{
// ── Registration ────────────────────────────────────────────────────────
[Test]
public void TryAdd_NewKey_SucceedsAndIsRetrievable()
{
var lib = new ProviderLibrary();
var p = new StubProvider("FuncA()", GuidA);
Assert.IsTrue(lib.TryAdd(p));
Assert.IsTrue(lib.TryGet<IDefinition>("FuncA()", out var found));
Assert.AreSame(p, found);
}
[Test]
public void TryAdd_DuplicateKey_ReturnsFalse_BothSuppressed()
{
var lib = new ProviderLibrary();
lib.TryAdd(new StubProvider("FuncA()", GuidA));
var result = lib.TryAdd(new StubProvider("FuncA()", GuidB));
Assert.IsFalse(result);
Assert.IsFalse(lib.TryGet<IDefinition>("FuncA()", out _));
Assert.IsEmpty(lib.ProvidersByVersion<IDefinition>("FuncA()").ToList());
}
[Test]
public void TryAdd_ThirdRegistrationOfConflictedKey_StaysConflicted()
{
var lib = new ProviderLibrary();
lib.TryAdd(new StubProvider("FuncA()", GuidA));
lib.TryAdd(new StubProvider("FuncA()", GuidB));
lib.TryAdd(new StubProvider("FuncA()", GuidC));
Assert.IsFalse(lib.TryGet<IDefinition>("FuncA()", out _));
}
[Test]
public void TryAdd_MultipleDistinctKeys_AllRetrievable()
{
var lib = new ProviderLibrary();
var pA = new StubProvider("FuncA()", GuidA);
var pB = new StubProvider("FuncB()", GuidA);
var pC = new StubProvider("FuncC()", GuidB);
lib.TryAdd(pA); lib.TryAdd(pB); lib.TryAdd(pC);
Assert.IsTrue(lib.TryGet<IDefinition>("FuncA()", out var a)); Assert.AreSame(pA, a);
Assert.IsTrue(lib.TryGet<IDefinition>("FuncB()", out var b)); Assert.AreSame(pB, b);
Assert.IsTrue(lib.TryGet<IDefinition>("FuncC()", out var c)); Assert.AreSame(pC, c);
}
// ── Clear ────────────────────────────────────────────────────────────────
[Test]
public void Clear_RemovesAllProvidersFromAsset()
{
var lib = new ProviderLibrary();
lib.TryAdd(new StubProvider("FuncA()", GuidA));
lib.TryAdd(new StubProvider("FuncB()", GuidA));
lib.TryAdd(new StubProvider("FuncC()", GuidB));
lib.ClearByAssetID(GuidA);
Assert.IsFalse(lib.TryGet<IDefinition>("FuncA()", out _));
Assert.IsFalse(lib.TryGet<IDefinition>("FuncB()", out _));
Assert.IsTrue(lib.TryGet<IDefinition>("FuncC()", out _));
}
[Test]
public void Clear_OneOfConflictingAssets_SurvivorRetrievable()
{
var lib = new ProviderLibrary();
var survivor = new StubProvider("FuncA()", GuidB);
lib.TryAdd(new StubProvider("FuncA()", GuidA));
lib.TryAdd(survivor);
Assert.IsFalse(lib.TryGet<IDefinition>("FuncA()", out _), "Expected conflict while both present.");
lib.ClearByAssetID(GuidA);
Assert.IsTrue(lib.TryGet<IDefinition>("FuncA()", out var found));
Assert.AreSame(survivor, found);
}
[Test]
public void Clear_BothConflictingAssets_RemovesKeyEntirely()
{
var lib = new ProviderLibrary();
lib.TryAdd(new StubProvider("FuncA()", GuidA));
lib.TryAdd(new StubProvider("FuncA()", GuidB));
lib.ClearByAssetID(GuidA);
lib.ClearByAssetID(GuidB);
Assert.IsFalse(lib.TryGet<IDefinition>("FuncA()", out _));
Assert.IsEmpty(lib.ProvidersByVersion<IDefinition>("FuncA()").ToList());
}
[Test]
public void Clear_UnknownAsset_DoesNotThrow()
{
var lib = new ProviderLibrary();
Assert.DoesNotThrow(() => lib.ClearByAssetID(GuidA));
}
[Test]
public void Clear_ThenReAdd_NewProviderIsRetrievable()
{
var lib = new ProviderLibrary();
lib.TryAdd(new StubProvider("FuncA()", GuidA));
lib.ClearByAssetID(GuidA);
var fresh = new StubProvider("FuncA()", GuidB);
Assert.IsTrue(lib.TryAdd(fresh));
Assert.IsTrue(lib.TryGet<IDefinition>("FuncA()", out var found));
Assert.AreSame(fresh, found);
}
// ── Conflict suppression across keys ────────────────────────────────────
[Test]
public void TryAdd_ConflictedKey_DoesNotMaskSiblingKey()
{
var lib = new ProviderLibrary();
var unique = new StubProvider("FuncB()", GuidB);
lib.TryAdd(new StubProvider("FuncA()", GuidA));
lib.TryAdd(new StubProvider("FuncA()", GuidC));
lib.TryAdd(unique);
Assert.IsFalse(lib.TryGet<IDefinition>("FuncA()", out _), "FuncA conflict suppresses the key.");
Assert.IsTrue(lib.TryGet<IDefinition>("FuncB()", out var found), "FuncB is unrelated and stays retrievable.");
Assert.AreSame(unique, found);
}
}
[TestFixture]
class ProviderLibraryProvidersByVersionTests : ProviderLibraryTestFixtureBase
{
[Test]
public void ProvidersByVersion_UnknownKey_ReturnsEmpty()
{
var lib = new ProviderLibrary();
Assert.IsEmpty(lib.ProvidersByVersion<IShaderFunction>("Missing()").ToList());
}
[Test]
public void ProvidersByVersion_SingleRegistration_ReturnsIt()
{
var lib = new ProviderLibrary();
var p = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
lib.TryAdd(p);
var results = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(1, results.Count);
Assert.AreSame(p, results[0]);
Assert.AreEqual("1.0", results[0].Version);
}
[Test]
public void ProvidersByVersion_ConflictedKey_ReturnsEmpty()
{
var lib = new ProviderLibrary();
// No sg:Version hints — same key without versions is a true conflict.
var pA = new ReflectedFunctionProvider(GuidA, FuncNoVersion("FuncA"));
var pB = new ReflectedFunctionProvider(GuidB, FuncNoVersion("FuncA"));
lib.TryAdd(pA);
lib.TryAdd(pB);
// Conflicted keys are suppressed consistently across all library query surfaces.
Assert.IsFalse(lib.TryGet<IShaderFunction>("FuncA()", out _));
Assert.IsEmpty(lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList());
}
[Test]
public void ProvidersByVersion_VersionedKey_ReturnsAll_NewestFirst()
{
var lib = new ProviderLibrary();
var pA = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var pB = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "2.0"));
lib.TryAdd(pA);
lib.TryAdd(pB);
var results = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(2, results.Count);
Assert.AreSame(pB, results[0], "Newest version should sort first.");
Assert.AreEqual("2.0", results[0].Version);
Assert.AreEqual("1.0", results[1].Version);
}
[Test]
public void ProvidersByVersion_ProviderWithoutVersionHint_ReportsKUnversioned()
{
var lib = new ProviderLibrary();
var p = new ReflectedFunctionProvider(GuidA, FuncNoVersion("FuncA"));
lib.TryAdd(p);
var results = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(1, results.Count);
Assert.AreEqual(Version.kUnversioned, results[0].Version);
}
}
[TestFixture]
class ProviderLibraryVersionTests : ProviderLibraryTestFixtureBase
{
// ── TryAdd — versioned coexistence ────────────────────────────────────
// Basic "two distinct versions coexist, newest sorts first" coverage lives in
// ProvidersByVersion_VersionedKey_ReturnsAll_NewestFirst above. This fixture
// covers cases that go beyond that: three versions, unversioned coexistence,
// same-version conflict, partial conflicts, and per-version Clear scoping.
[Test]
public void TryAdd_ThreeDistinctVersions_LatestSortsFirst()
{
var lib = new ProviderLibrary();
var p1 = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var p15 = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "1.5"));
var p2 = new ReflectedFunctionProvider(GuidC, FuncWithVersion("FuncA", "2.0"));
lib.TryAdd(p1); lib.TryAdd(p15); lib.TryAdd(p2);
var byVersion = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(3, byVersion.Count);
Assert.AreSame(p2, byVersion[0], "Expected the 2.0 provider as the latest.");
}
[Test]
public void TryAdd_SameKey_SameVersion_IsConflict()
{
var lib = new ProviderLibrary();
lib.TryAdd(new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0")));
var result = lib.TryAdd(new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "1.0")));
Assert.IsFalse(result);
Assert.IsFalse(lib.TryGet<IShaderFunction>("FuncA()", out _));
}
[Test]
public void TryAdd_SameKey_OneWithoutVersion_Coexists()
{
// Unversioned providers participate in versioned coexistence — they sort as the
// oldest possible "version" (kUnversioned), so a versioned sibling shadows them
// as the newest. No conflict.
var lib = new ProviderLibrary();
var versioned = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var unversioned = new ReflectedFunctionProvider(GuidB, FuncNoVersion("FuncA"));
Assert.IsTrue(lib.TryAdd(versioned));
Assert.IsTrue(lib.TryAdd(unversioned));
// The versioned provider sorts first as the latest.
var byVersion = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(2, byVersion.Count);
Assert.AreSame(versioned, byVersion[0]);
}
// ── Clear — versioned key cleanup ─────────────────────────────────────
[Test]
public void Clear_VersionedKey_SingleSurvivor_IsNoLongerVersioned()
{
var lib = new ProviderLibrary();
var p1 = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var p2 = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "2.0"));
lib.TryAdd(p1);
lib.TryAdd(p2);
lib.ClearByAssetID(GuidB); // remove the 2.0 provider
// Single survivor — still retrievable, no longer versioned (just a normal single entry).
Assert.IsTrue(lib.TryGet<IShaderFunction>("FuncA()", out var found));
Assert.AreSame(p1, found);
Assert.AreEqual(1, lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList().Count);
}
// ── Per-version conflict scoping (TryAdd + Clear) ─────────────────────
//
// Conflict scoping is per-version: a collision on one version of a key must not
// suppress other versions, and Clear must re-evaluate conflict state on the
// remaining buckets without false resolution or lingering conflict.
[Test]
public void TryAdd_PartialConflict_OnlyAffectedVersionSuppressed()
{
var lib = new ProviderLibrary();
var pA = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var pB = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "1.0"));
var pC = new ReflectedFunctionProvider(GuidC, FuncWithVersion("FuncA", "2.0"));
lib.TryAdd(pA);
lib.TryAdd(pB); // collides with pA on v1.0
Assert.IsTrue(lib.TryAdd(pC), "v2.0 should register cleanly — unrelated to the v1.0 collision.");
// ProvidersByVersion skips the conflicted v1.0 bucket; pC (v2.0) is the only entry.
var byVersion = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(1, byVersion.Count);
Assert.AreSame(pC, byVersion[0]);
Assert.AreEqual("2.0", byVersion[0].Version);
}
[Test]
public void Clear_PartialConflict_ResolvesOnlyAffectedVersion()
{
var lib = new ProviderLibrary();
var pA = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var pB = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "1.0"));
var pC = new ReflectedFunctionProvider(GuidC, FuncWithVersion("FuncA", "2.0"));
lib.TryAdd(pA);
lib.TryAdd(pB);
lib.TryAdd(pC);
// Removing one of the v1.0 collisions resolves the v1.0 conflict alone — v2.0
// is untouched and still sorts first as the newest.
lib.ClearByAssetID(GuidB);
var byVersion = lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList();
Assert.AreEqual(2, byVersion.Count);
Assert.AreSame(pC, byVersion[0]);
Assert.AreEqual("2.0", byVersion[0].Version);
Assert.AreEqual("1.0", byVersion[1].Version);
}
// Two versioned + one unversioned coexist freely. Clearing the unversioned one leaves
// the versioned pair intact and continues to pick the newest as the representative.
[Test]
public void Clear_UnversionedAmongVersioned_LeavesVersionedNewestAccessible()
{
var lib = new ProviderLibrary();
var pA = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var pB = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "2.0"));
var pC = new ReflectedFunctionProvider(GuidC, FuncNoVersion("FuncA"));
Assert.IsTrue(lib.TryAdd(pA));
Assert.IsTrue(lib.TryAdd(pB));
Assert.IsTrue(lib.TryAdd(pC));
// All three coexist — pB (v2.0) sorts first as the newest representative
// even with pC present.
Assert.AreSame(pB, lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList()[0]);
lib.ClearByAssetID(GuidC);
Assert.AreSame(pB, lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList()[0],
"Newest versioned provider should remain accessible after clearing the unversioned one.");
}
// When the remaining Count > 1 providers still don't form a valid versioned set
// (duplicate versions), the entry must stay conflicted — no false resolution.
[Test]
public void Clear_ConflictedEntry_DuplicateVersionsRemain_StaysConflicted()
{
var lib = new ProviderLibrary();
var pA = new ReflectedFunctionProvider(GuidA, FuncWithVersion("FuncA", "1.0"));
var pB = new ReflectedFunctionProvider(GuidB, FuncWithVersion("FuncA", "2.0"));
var pC = new ReflectedFunctionProvider(GuidC, FuncWithVersion("FuncA", "1.0")); // same version as pA
lib.TryAdd(pA);
lib.TryAdd(pB); // versioned coexistence so far
lib.TryAdd(pC); // duplicate v1.0 → conflict
lib.ClearByAssetID(GuidB); // remove pB (v2.0), leaving pA(v1.0) and pC(v1.0) — still conflicted
Assert.IsEmpty(lib.ProvidersByVersion<IShaderFunction>("FuncA()").ToList(), "Duplicate versions must keep the entry conflicted.");
Assert.IsFalse(lib.TryGet<IShaderFunction>("FuncA()", out _), "TryGet must return false for a still-conflicted key.");
}
}
// Reload's only job is to resolve the serialized (m_providerKey, m_sourceAssetId) pair
// via TryResolve, or leave m_definition null on failure. Migration after a function
// moves between assets is handled out-of-band by the ProviderLibrary.OnProvidersReloaded
// listener in ShaderGraph, which patches the saved graph file directly — that path is
// covered by PostProcessorIntegrationTests, not here.
[TestFixture]
class ReflectedFunctionProviderReloadTests
{
// A GUID that points to no real asset — TryResolve will always fail for it.
static readonly GUID DeadGuid = new GUID("00000000000000000000000000000001");
static readonly FieldInfo s_defField =
typeof(ReflectedFunctionProvider).GetField("m_definition", BindingFlags.Instance | BindingFlags.NonPublic);
// Constructor wiring of the Version hint into the provider's Version property is
// covered by ProvidersByVersion_SingleRegistration_ReturnsIt and
// ProvidersByVersion_VersionIsUnversionedWhenNotDeclared above, which exercise the
// same behaviour through the public API rather than reflecting into private state.
[Test]
public void Reload_SerializedAssetDoesNotResolve_LeavesDefinitionNull()
{
var def = new ShaderFunction("TestFunc", null, null, new ShaderType("float"), "return 1;", null);
var stale = new ReflectedFunctionProvider(DeadGuid, def);
s_defField.SetValue(stale, null); // force Reload
Assert.IsNull(stale.Definition,
"Reload must not consult the library — it either resolves via the serialized GUID or returns null.");
}
}
[TestFixture]
class GroupKeyHintTests
{
// Exercises GroupKey.Initialize directly — no library needed, no logs expected.
static GroupKey ParseGroupKey(string rawValue)
{
var hint = new GroupKey();
hint.Initialize(true, rawValue, null, null, Func.kGroupKey, new Dictionary<string, string>());
return hint;
}
[Test]
public void GroupKey_OneToken_ParsesKeyOnly()
{
// DisplayName defaults to the group key when no third token is provided.
var hint = ParseGroupKey("BlendOp");
Assert.IsTrue(hint.IsResolved);
Assert.AreEqual("BlendOp", hint.Value);
Assert.AreEqual(0, hint.Priority);
Assert.AreEqual("BlendOp", hint.DisplayName);
Assert.IsNull(hint.Label);
}
[Test]
public void GroupKey_TwoTokens_ParsesKeyAndPriority()
{
var hint = ParseGroupKey("BlendOp, 10");
Assert.IsTrue(hint.IsResolved);
Assert.AreEqual("BlendOp", hint.Value);
Assert.AreEqual(10, hint.Priority);
Assert.AreEqual("BlendOp", hint.DisplayName);
Assert.IsNull(hint.Label);
}
[Test]
public void GroupKey_ThreeTokens_ParsesKeyPriorityAndDisplayName()
{
var hint = ParseGroupKey("BlendOp, 10, Blend");
Assert.IsTrue(hint.IsResolved);
Assert.AreEqual("BlendOp", hint.Value);
Assert.AreEqual(10, hint.Priority);
Assert.AreEqual("Blend", hint.DisplayName);
Assert.IsNull(hint.Label);
}
[Test]
public void GroupKey_FourTokens_ParsesAllFields()
{
var hint = ParseGroupKey("BlendOp, 10, Blend, Blend Mode");
Assert.IsTrue(hint.IsResolved);
Assert.AreEqual("BlendOp", hint.Value);
Assert.AreEqual(10, hint.Priority);
Assert.AreEqual("Blend", hint.DisplayName);
Assert.AreEqual("Blend Mode", hint.Label);
}
[Test]
public void GroupKey_FiveTokens_ErrorsAndDoesNotResolve()
{
var hint = ParseGroupKey("BlendOp, 10, Blend, Blend Mode, Extra");
Assert.IsFalse(hint.IsResolved);
Assert.IsNotNull(hint.Message);
}
[Test]
public void GroupKey_NonIntegerPriority_ErrorsAndDoesNotResolve()
{
var hint = ParseGroupKey("BlendOp, notanint");
Assert.IsFalse(hint.IsResolved);
Assert.IsNotNull(hint.Message);
}
[Test]
public void GroupKey_NegativePriority_IsValid()
{
var hint = ParseGroupKey("BlendOp, -5");
Assert.IsTrue(hint.IsResolved);
Assert.AreEqual(-5, hint.Priority);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 366c9e15ecf02a34191e749ec109fd96
@@ -1,23 +0,0 @@
{
"name": "Unity.GraphAuthoring.Editor.Tests",
"references": [
"Unity.GraphAuthoring.Editor",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 11e54bd74ece90448b4934d1426aa761
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
{
"name": "com.unity.graph-authoring",
"displayName": "Graph Authoring",
"version": "1.0.0",
"description": "Shared graph tooling for Shader Graph and Visual Effect Graph.",
"_fingerprint": "f8926ccf2c8fe9afa7c1af7de1f013ddf7de6436"
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: e249558d9733962439006f1c00140d66
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,110 +0,0 @@
# Changelog
## [1.4.0] - 2025-10-03
Code from this package is moved into Module and it's available in Unity Editor.
### Fixed
* Fixed `math.hash` crash when using IL2CPP builds on Arm 32 bit devices.
* Fixed obsolete method usage warnings for `MatrixDrawer.CanCacheInspectorGUI` and `PrimitiveVectorDrawer.CanCacheInspectorGUI` in UNITY_2023_2_OR_NEWER.
## [1.3.1] - 2023-07-12
### Added
* Added `math.square` to compute the square (x * x).
* Added `math.orthonormal_basis` to compute an orthonormal basis from a single unit length vector.
* Added `math.sign` for int, int2, int3 and int4.
* Added `math.chgsign` for float, float2, float3, and float4.
* Added `math.Euler` to convert a quaternion to Euler angles.
* Added `math.angle` to compute the angle between two unit quaternions.
* Added `math.rotation` to extract a quaternion rotation from a float3x3 (that may have scale).
* Added `math.mulScale` to scale columns of a float3x3 with scaling coefficients in a float3.
* Added `math.scaleMul` to scale rows of a float3x3 with scaling coefficients in a float3.
* Added `AffineTransform` type.
* Added `PI2`, `PIHALF`, `TAU`, `TODEGREES` and `TORADIANS` constants.
### Changed
* `asfloat(uint)`, `asuint(float)`, `asint(float)` and other related methods are now faster in mono without Burst. Other methods which use these will see a performance improvement.
* Modified `quaternion.nlerp` to be branchless.
* More descriptive parameter names for many methods in `math` class.
* Made `Il2CppEagerStaticClassConstructionAttribute` internal to avoid conflicts with other definitions outside of the package.
## [1.2.6] - 2022-02-11
### Changed
* Made Il2CppEagerStaticClassConstructionAttribute internal to avoid conflicts with other definitions outside of the package.
## [1.2.5] - 2021-11-01
### Fixed
* Fixed property drawing when manually drawing a property that was hidden with [HideInInspector].
## [1.2.4] - 2021-09-22
### Added
* Added `[Il2CppEagerStaticClassConstruction]` to Unity.Mathematics types to run static constructors at startup. This improves IL2CPP performance slightly for types that have static constructors.
### Changed
* License file updated to satisfy Unity's package validation tests.
* Changed noise documentation in comments to xmldoc comments.
### Fixed
* Fixed Equals(object) override which did not check type before casting. This could cause exceptions to be thrown when the object did not match the expected type.
* Fixed incorrect `math.tzcnt` documentation which mentioned leading zero counts instead of trailing zero counts.
* Fixed `float2x2.Rotate` documentation to mention radians instead of degrees.
* Fixed documentation for methods and properties that were previously undocumented.
## [1.2.1] - 2020-08-06
### Fixed
* Fixed warnings for meta files existing even though the files they represent did not exist.
### Internal (Not ready for production)
## [1.2.0] - 2020-08-03
### Added
* Added `[MethodImpl(MethodImplOptions.AggressiveInlining)]` to many static functions to improve IL2CPP performance.
* Added `compress()` that accepts a `float4` and `uint4`.
* Added `math.project()` and `math.projectsafe()` for vector projection.
* Added `math.EPSILON`, `math.INFINITY`, `math.NAN` and their double counterparts.
* Added `[Serializable]` to `RigidTransform`.
* Added `math.ceillog2()`.
* Added `math.floorlog2()`.
* Added `math.down()`, `math.forward()`, etc for Cartesian coordinate axes that match UnityEngine Vector3 equivalents.
* Added `math.ispow2()`.
* Added `half.MinValueAsHalf` and `half.MaxValueAsHalf` to avoid having to explicitly convert from float.
* Added a `float3x3` constructor which takes a `float4x4` as input.
* Added `[Serializable]` to half types.
* Added some performance tests which can be run from the Unity test project.
* Added `Random.CreateFromIndex()` to assist in creating Random instances from loop indices.
### Fixed
* Fixed documentation bug where `quaternion.RotateX/Y/Z` referred to a `float4x4` instead of quaternion.
* Fixed code generation bugs which could cause Windows and Mac to generate different test code.
* Fixed some test asserts which used NaNs and signed zeros which failed in IL2CPP builds.
* Updated documentation for `math.countbits()` to include equivalent names on Intel and ARM architectures to aid in discoverability.
### Internal (Not ready for production)
* Added `Unity.Mathematics.Geometry.Plane` to represent planes in 3D space.
* Added more `MinMaxAABB` functionality from `Unity.Physics.Aabb`.
* Added `Unity.Mathematics.Geometry.Math` to hold static functions like AABB transformations.
* Added `MinMaxAABB`.
## [1.1.0] - 2019-07-08
- Release stable version
## [1.1.0-preview.1] - 2019-06-27
- Add new math.bitmask to return a bit mask from a bool4
## [1.0.1] - 2019-04-15
- Release stable version
- Modify all math constants (e.g `math.PI`) to provide float constant by default instead of double. Use for example `math.PI_DBL` to get the previous double constant.
## [1.0.0-preview.1] - 2019-02-28
- Fixed bug where modifications on prefabs could not be reverted for vector properties when using context menu in Inspector.
- Fixed structure of the package for internal validation
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 56de866d5032f461baa43decd9ce3bd8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,67 +0,0 @@
Document written by Rune Stubbe, December 14th 2018
Pseudorandom notes on Unity.Mathematics
==============================================
Differences between Mathematics and HLSL
-----------------------------------------
Unity.Mathematics tries to follow familiar HLSL conventions whenever possible. Formulas and computations are generally more succintly expressed in a HLSL-like syntax with swizzles and free function than the traditional C# Unity Math library. Unity.Mathematics is not HLSL, but is in most instances trivially portable. The large common subset allows for code to directly shared between the two in many cases.
Due to inherent limitations in C# and for consistency reasons there are some notable differences between Unity.Mathematics and HLSL.
1. Type conversion rules:
HLSL will implicitly perform narrowing conversions. This could for instance be a float2 assigned directly to an int2/bool2.
C# requires explict casts to perform these kinds of potentially lossy conversions. We have chosen to follow the C# conventions here for consistency with how the conversions already function with C#'s builtin types int/float/etc.
2. Floating point literals:
In HLSL the default floating point literal precision is float, while it is double in C#. The `1.5` float literal in HLSL corresponds to `1.5f` in C# and the likewise the double literal `3.2L` in HLSL corresponds to `3.2` in C#.
3. Half precision floating point: `half`/`halfN` are supported in Mathematics, but only as storage types that implicitly convert to `float`/`double` types and can be explicitly converted to from these types. No arithmetic operation are defined on them as opposed to the HLSL types. We chose to do this as these operations would involve implicit conversion to and from `half`. These operations would be expensive and we believe that expensive operations should not happen implicitly.
4. Implicit truncation:
HLSL allows you to assign a float4 to a float2. As long as the source has at least as many components as the destination, the assignment is allowed. Any additional components are truncated away. We consider this behavior counter intuitive and error prone, so Unity.Mathematics does not try to mimic this behavior.
Unity.Mathematics aims to be a lean core library without domain specific features such as collision detection primitives, color space conversions, etc. Within these constraints, we decided to make the following additions.
1. Named constructors for matrix types:
A lot of game code is centered around manipulating transforms. To ease this work we have added a number of constructors and conversions between various transform representations and added a number of named constructors for constructing various transforms for Rotation, Translation, Scaling, Orthographic/Perspective projections, LookAt transforms, etc.
2. A `quaternion` type:
Considering how ubiquitous quaternions are in games, it seems appropriate to provide a good standard implementation in core mathematics. Whenever possible `quaternion` is a drop in replacement for `float3x3`. `mul` is also defined for `quaternion` just as for `float3x3`, `quaternion` can be used to initialize matrices and `quaternion` defines the same set of named constructors as the equivalent `float3x3` type.
3. Various additional utility functions:
`inverse`/`fastinverse` for inverting a matrix or quaternion. Horizontal operations `csum`/`cmin`/`cmax`. `compress` for left packing a vector using a boolean mask. Squared length/distance functions `lengthsq/distancesq`. `orthonormalize` functions, and many others.
4. Random Number Generator: `Random`. Basic PRNG with support for drawing uniformly random values of the basic scalar and vector types. It also has functions for generating uniformly random directions and rotation. The generator is based on the xor-shift algorithm. This algorithm was chosen primarily because it has a small state and unlike most PRNGs doesn't require integer multiplication, which is a problematic operation on lower-tier vector instruction sets.
5. `RigidTransform` (quaternion + position) and Cellular/Perlin Noise: The current math library includes these, but while useful, they would would probably find a better home somewhere outside the core mathematics library.
Generated code
--------------
Unity.Mathematics has a high degree of regularity that is not easily expressed in ordinary C# code. Constructors, conversion operators, arithmetic operators, swizzles, ToString-methods, Hash functions, etc. of the many vector and matrix type variants are all distinct, but all follow the same basic structure. To avoid the error prone process of writing and maintaining the many variants, we generate the code for these regular functions using the C# program **Unity.Mathematics.CodeGen**. The resulting generated files are easily recognizable by their *.gen.cs extension. To get test coverage of these functions, the corresponding tests are also generated by this program.
Tests
---
Unity.Mathematics includes thousands of unit tests to cover methods and operator overloads for the vector, matrix and quaternion types that can be found in the **Unity.Mathematics.Tests** project.
The tests aim to exercise the entire set of Unity.Mathematics features using a combination of generated and manual tests. As Unity.Mathematics is intended to be used with the Burst compiler, we also include most of these tests as part of the Burst compiler tests in the **Burst.Compiler.IL.Tests** project. All shared tests between the two are in the `Tests/Shared` folder. A few tests have had to be omitted as the testing code it self uses unburstable code.
Floating point precision and floating point overflow/underflow
--------------------------------------------------------------
Floating point precision in C# guarantees a minimum, but not a maximum precision. float might be evaluated as double (which mono likes to do) or double might be evaluated using extended precision (80bits). The precision is not even guaranteed to be consistent on a specific platform or even a concrete program. It might vary depending on how the JIT decides to optimize a particular piece of code!
Anecdotely it seems these two otherwise semantically identical pieces of code can behave differently because of JIT optimization decisions (x86):
1. Constants in array
```C#
var a = new double[2] = { -2.5e160, 1.2e160 };
double d = System.Math.Sqrt(a[0] * a[0] + a[1] * a[1]);
System.Console.WriteLine("" + d); // Debug Build: Infinity, Release Build: 2.86356e160.
```
2. Constants in local variables
```C#
double a = -2.6e160;
double b = -1.2e160;
double d = System.Math.Sqrt(a * a + b * b);
System.Console.WriteLine("" + d); // Debug Build: Infinity, Release Build: Infinity.
```
You would expect the result to always be Infinity as the intermediate value inside the Sqrt (8.2e320) is outside the valid double precision range. But depending on the platform, whether optimizations are enabled and surprisingly even whether or not the constants come from a local variable or array, this might produce a finite value. Seemingly this happens because it might decide to perform the calculation in extended precision.
This behavior is specified in the C# specification (9.3.7) where it states: "Floating-point operations may be performed with higher precision than the result type of the operation.".
Due to this inconsistency, we have to avoid floating point overflow behavior, as it is simple not guaranteed even using Microsoft's own runtime!
Signed zeros
-------------
According to the C# specification section 9.3.7 C# supports and distinguishes between positive and negative floating point zeros. In practice we have found this not to be the case on our current Linux Mono environment, where seemingly negative zeroes are initialized as positive zeros. We should investigate this further at some point, but so far the affected tests are simply disabled in Linux using the `[WindowsOnlyAttribute]`.
Known issues
------------
1. In order for code to be burstable we have to define all class-like objects as structs, but unlike classes there is no way to enforce that a struct gets initialized in any particular way. Just declaring a struct will give the user a zero-initialized instance. One instance where this limitation is very apparent is with `Random`. There is no way for us to guarantee that the user doesn't accidentally declare a zero-initialized instance. Such an instance has the unfortunate property that it will be stuck perpetually generating nothing but zeros. This is by the nature of the xor-shift algorithm, but it seems unnecessarily error-prone. While we could work around the problem by internally remapping the seed space, it would incur some unnecessary additional overhead and it seems this problem is likely to arise in other class-like objects implemented with structs.
@@ -1,4 +0,0 @@
com.unity.mathematics copyright © 2023 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: a7f87992cd1d44040ae42b05e5fbdaa5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 8a512234959c84f759a00b7053a0169b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 4598ea5d00316445abd4ab7d202861fd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,19 +0,0 @@
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
/// <summary>
/// WARNING: Dummy Test needed for package validation tests
/// DO NOT REMOVE
/// </summary>
namespace Unity.Mathematics.Editor.Tests
{
internal class BasicTestSuite
{
[Test]
public void BasicTest()
{
Assert.True(true);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 30a89e6ba963248cf85628ef0bbdab27
@@ -1,21 +0,0 @@
{
"name": "Unity.Mathematics.Editor.Tests",
"references": [
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [
],
"autoReferenced": true,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": []
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6f96e4b9bbe444c8f88d51ae10a2b75e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 04270704ae4dc994cb99e586b2ddf5e0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,81 +0,0 @@
using System.Runtime.CompilerServices;
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.AffineTransform))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool2x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool2x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool2x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool3x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool3x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool3x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool4x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool4x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.bool4x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.DoNotNormalizeAttribute))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double2x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double2x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double2x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double3x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double3x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double3x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double4x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double4x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.double4x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float2x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float2x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float2x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float3x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float3x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float3x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float4x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float4x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.float4x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.half))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.half2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.half3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.half4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int2x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int2x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int2x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int3x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int3x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int3x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int4x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int4x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.int4x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.math))]
/*[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.math.RotationOrder))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.math.ShuffleComponent))]*/
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.noise))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.PostNormalizeAttribute))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.quaternion))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.Random))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.RigidTransform))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.svd))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint2x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint2x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint2x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint3x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint3x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint3x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint4x2))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint4x3))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.uint4x4))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.Geometry.Math))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.Geometry.MinMaxAABB))]
[assembly: TypeForwardedTo(typeof(global::Unity.Mathematics.Geometry.Plane))]
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 01a36851f94381c4fad0b71ba7852499
@@ -1,4 +0,0 @@
{
"name": "Unity.Mathematics",
"allowUnsafeCode": true
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: d8b63aba1907145bea998dd612889d6b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,10 +0,0 @@
{
"name": "com.unity.mathematics",
"displayName": "Mathematics",
"version": "1.4.0",
"description": "Note: The package is now empty and unnecessary, because it has been moved to a module. Formerly, it contained Unity's C# SIMD math library providing vector types and math functions with a shader like syntax.",
"keywords": [
"unity"
],
"_fingerprint": "9c2fa23d2d8206fd3b8640af68134f7c214f2440"
}
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: d34d0af25c21141a98c0cf3c0066444f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
sub-package.*
upm-ci~/**
.Editor/**
.yamato/**
*.zip*
TestRunnerOptions.json
.idea/**
@@ -1,36 +0,0 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
Version Updated
The version number for this package has increased due to a version update of a related graphics package.
## [17.5.0] - 2026-02-18
This version is compatible with Unity 6000.6.0a7.
For the release notes, refer to the [Unity download archive](https://unity.com/releases/editor/archive).
## [17.4.0] - 2025-10-22
This version is compatible with Unity 6000.4.0a4.
For the release notes, refer to the [Unity download archive](https://unity.com/releases/editor/archive).
## [17.0.3] - 2025-08-27
This version is compatible with Unity 6000.3.0b1.
For the release notes, refer to the [Unity download archive](https://unity.com/releases/editor/archive).
## [17.0.1] - 2023-12-21
This version is compatible with Unity 2023.3.0b2.
Version Updated
The version number for this package has increased due to a version update of a related graphics package.
Started Changelog
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5e031dc4f3118ad44b907737119d7ed1
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,7 +0,0 @@
---
uid: urp-config-api-index
---
# Universal Render Pipeline Configuration package scripting API
This is the documentation for the scripting APIs of the Universal Render Pipeline (URP) Configuration package.
@@ -1,6 +0,0 @@
# Universal Render Pipeline Configuration Package
The Universal Render Pipeline (URP) uses this package to control the settings of some of its features. If you want to use this package to configure URP, you must link it as a local package.
* For information on how to set up and use the URP Config package, see [URP Config](https://docs.unity3d.com/Manual/urp/URP-Config-Package.html).
* For documentation on URP itself, see [URP documentation](https://docs.unity3d.com/Manual/urp/urp-introduction.html).
@@ -1,5 +0,0 @@
com.unity.render-pipelines.universal-config copyright © 2020 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 8a8d9e4408d3f1247884b71451a0abcf
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 670b80493e242394ba4eda64f09253ac
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,28 +0,0 @@
//-----------------------------------------------------------------------------
// Configuration
//-----------------------------------------------------------------------------
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Project-wide shader configuration options.
/// </summary>
/// <remarks>This enum will generate the proper shader defines.</remarks>
///<seealso cref="ShaderConfig"/>
[GenerateHLSL]
public static class ShaderOptions
{
/// <summary>Max number of lights supported on mobile with OpenGL 3.0 and below.</summary>
public const int k_MaxVisibleLightCountLowEndMobile = 16;
/// <summary>Max number of lights supported on mobile, OpenGL, and WebGPU platforms.</summary>
public const int k_MaxVisibleLightCountMobile = 32;
/// <summary>Max number of lights supported on desktop platforms.</summary>
public const int k_MaxVisibleLightCountDesktop = 256;
/// <summary> Switch fog keywords (FOG_LINEAR, FOG_EXP and FOG_EXP2) to dynamic branching.</summary>
/// <remarks> For more information on dynamic branches, refer to [Shader Branching](https://docs.unity3d.com/Manual/shader-branching.html).</remarks>
public const int k_UseDynamicBranchFogKeyword = 0;
}
}
Loaded 100 of 3902 files, more files were not shown because too many files have changed in this diff. Show more