Resolve WES-90 "Integrate signpredictor in courses"
This commit is contained in:
committed by
Jerome Coudron
parent
1a75791d62
commit
746906294b
148
Packages/com.unity.barracuda/Editor/BarracudaAnalytics.cs
Normal file
148
Packages/com.unity.barracuda/Editor/BarracudaAnalytics.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Onnx;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Analytics;
|
||||
|
||||
namespace Unity.Barracuda.Editor
|
||||
{
|
||||
internal class BarracudaAnalytics
|
||||
{
|
||||
static bool s_EventRegistered = false;
|
||||
const int k_MaxEventsPerHour = 1000;
|
||||
const int k_MaxNumberOfElements = 1000;
|
||||
const string k_VendorKey = "unity.barracuda";
|
||||
const string k_ImportEventName = "uBarracudaImport";
|
||||
|
||||
static bool EnableAnalytics()
|
||||
{
|
||||
AnalyticsResult result = EditorAnalytics.RegisterEventWithLimit(k_ImportEventName, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey);
|
||||
if (result == AnalyticsResult.Ok)
|
||||
s_EventRegistered = true;
|
||||
|
||||
return s_EventRegistered;
|
||||
}
|
||||
|
||||
struct BarracudaImportAnalyticsData
|
||||
{
|
||||
public string model_type;
|
||||
public string original_layers;
|
||||
public string imported_layers;
|
||||
public string import_warnings;
|
||||
}
|
||||
|
||||
public static void SendBarracudaImportEvent(object originalModel, Model importedModel)
|
||||
{
|
||||
//The event shouldn't be able to report if this is disabled but if we know we're not going to report
|
||||
//Lets early out and not waste time gathering all the data
|
||||
if (!EditorAnalytics.enabled)
|
||||
return;
|
||||
|
||||
if (!EnableAnalytics())
|
||||
return;
|
||||
|
||||
|
||||
var data = new BarracudaImportAnalyticsData();
|
||||
|
||||
try
|
||||
{
|
||||
data.original_layers = AnalyzeONNXModel(originalModel);
|
||||
data.imported_layers = AnalyzeNNModel(importedModel);
|
||||
data.model_type = string.IsNullOrEmpty(data.original_layers) ? "NN" : "ONNX";
|
||||
data.import_warnings = AnalyzeWarnings(importedModel);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
D.LogError($"Failed collecting Barracuda analytics: {e}");
|
||||
}
|
||||
|
||||
EditorAnalytics.SendEventWithLimit(k_ImportEventName, data);
|
||||
}
|
||||
|
||||
static string AnalyzeONNXModel(object originalModel)
|
||||
{
|
||||
if (!(originalModel is ModelProto))
|
||||
return "";
|
||||
|
||||
var layers = new Dictionary<string, int>();
|
||||
|
||||
var onnxModel = originalModel as ModelProto;
|
||||
foreach (var node in onnxModel.Graph.Node)
|
||||
{
|
||||
var layerDescription = node.OpType;
|
||||
|
||||
if (!layers.ContainsKey(layerDescription))
|
||||
layers[layerDescription] = 1;
|
||||
else
|
||||
layers[layerDescription] += 1;
|
||||
}
|
||||
|
||||
return DictionaryToJson(layers);
|
||||
}
|
||||
|
||||
static string AnalyzeNNModel(Model importedModel)
|
||||
{
|
||||
var layers = new Dictionary<string, int>();
|
||||
|
||||
foreach (Layer layer in importedModel.layers)
|
||||
{
|
||||
var layerDescription = LayerToString(layer);
|
||||
|
||||
if (!layers.ContainsKey(layerDescription))
|
||||
layers[layerDescription] = 1;
|
||||
else
|
||||
layers[layerDescription] += 1;
|
||||
}
|
||||
|
||||
return DictionaryToJson(layers);
|
||||
}
|
||||
|
||||
static string LayerToString(Layer layer)
|
||||
{
|
||||
var layerDescription = layer.type.ToString();
|
||||
|
||||
if (layer.type == Layer.Type.Conv2D || layer.type == Layer.Type.Conv2DTrans ||
|
||||
layer.type == Layer.Type.Conv3D || layer.type == Layer.Type.Conv3DTrans ||
|
||||
layer.type == Layer.Type.DepthwiseConv2D)
|
||||
{
|
||||
layerDescription += "_" + ConvShapeToString(layer);
|
||||
}
|
||||
|
||||
if (layer.activation != Layer.Activation.None)
|
||||
layerDescription += "_" + layer.activation.ToString();
|
||||
|
||||
return layerDescription;
|
||||
}
|
||||
|
||||
static string ConvShapeToString(Layer layer)
|
||||
{
|
||||
if (layer.type == Layer.Type.Conv2D ||
|
||||
layer.type == Layer.Type.DepthwiseConv2D ||
|
||||
layer.type == Layer.Type.Conv2DTrans)
|
||||
return string.Join("_",
|
||||
layer.datasets.Where(d => d.name.EndsWith("/K")).Select(it =>
|
||||
$"{it.shape.kernelHeight}x{it.shape.kernelWidth}x{it.shape.kernelDepth}x{it.shape.kernelCount}"));
|
||||
|
||||
if (layer.type == Layer.Type.Conv3D ||
|
||||
layer.type == Layer.Type.Conv3DTrans)
|
||||
return string.Join("_",
|
||||
layer.datasets.Where(d => d.name.EndsWith("/K")).Select(it =>
|
||||
$"{it.shape.kernelSpatialDepth}x{it.shape.kernelHeight}x{it.shape.kernelWidth}x{it.shape.kernelDepth}x{it.shape.kernelCount}"));
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static string AnalyzeWarnings(Model importedModel)
|
||||
{
|
||||
return "[" + string.Join(",",importedModel.Warnings.Select(item => $"'{item.LayerName}:{item.Message}'")) + "]";
|
||||
}
|
||||
|
||||
static string DictionaryToJson(Dictionary<string, int> dict)
|
||||
{
|
||||
var entries = dict.Select(d => $"\"{d.Key}\":{string.Join(",", d.Value)}");
|
||||
return "{" + string.Join(",", entries) + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92cb0e57f8c0c4255a2d2d93f844424d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.unity.barracuda/Editor/NNModelIcon.png
Normal file
BIN
Packages/com.unity.barracuda/Editor/NNModelIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
106
Packages/com.unity.barracuda/Editor/NNModelIcon.png.meta
Normal file
106
Packages/com.unity.barracuda/Editor/NNModelIcon.png.meta
Normal file
@@ -0,0 +1,106 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8682ff569c4c7457a8a8e3a527aad537
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: -1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
63
Packages/com.unity.barracuda/Editor/NNModelImporter.cs
Normal file
63
Packages/com.unity.barracuda/Editor/NNModelImporter.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.IO;
|
||||
using Unity.Barracuda.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.AssetImporters;
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#endif
|
||||
|
||||
namespace Unity.Barracuda
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset Importer of barracuda models.
|
||||
/// </summary>
|
||||
[ScriptedImporter(3, new[] {"nn"})]
|
||||
public class NNModelImporter : ScriptedImporter {
|
||||
private const string iconName = "NNModelIcon";
|
||||
|
||||
private Texture2D iconTexture;
|
||||
|
||||
/// <summary>
|
||||
/// Scripted importer callback
|
||||
/// </summary>
|
||||
/// <param name="ctx">Asset import context</param>
|
||||
public override void OnImportAsset(AssetImportContext ctx)
|
||||
{
|
||||
var model = File.ReadAllBytes(ctx.assetPath);
|
||||
|
||||
// Analyze model and send analytics if enabled
|
||||
var nnModel = ModelLoader.Load(ctx.assetPath, skipWeights:true);
|
||||
BarracudaAnalytics.SendBarracudaImportEvent(null, nnModel);
|
||||
|
||||
var assetData = ScriptableObject.CreateInstance<NNModelData>();
|
||||
assetData.Value = model;
|
||||
assetData.name = "Data";
|
||||
assetData.hideFlags = HideFlags.HideInHierarchy;
|
||||
|
||||
var asset = ScriptableObject.CreateInstance<NNModel>();
|
||||
asset.modelData = assetData;
|
||||
ctx.AddObjectToAsset("main obj", asset, LoadIconTexture());
|
||||
ctx.AddObjectToAsset("model data", assetData);
|
||||
|
||||
ctx.SetMainObject(asset);
|
||||
}
|
||||
|
||||
private Texture2D LoadIconTexture()
|
||||
{
|
||||
if (iconTexture == null)
|
||||
{
|
||||
string[] allCandidates = AssetDatabase.FindAssets(iconName);
|
||||
|
||||
if (allCandidates.Length > 0)
|
||||
{
|
||||
iconTexture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(allCandidates[0]), typeof(Texture2D)) as Texture2D;
|
||||
}
|
||||
}
|
||||
return iconTexture;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.barracuda/Editor/NNModelImporter.cs.meta
Normal file
11
Packages/com.unity.barracuda/Editor/NNModelImporter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19ed1486aa27d4903b34839f37b8f69f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.unity.barracuda/Editor/ONNXModelIcon.png
Normal file
BIN
Packages/com.unity.barracuda/Editor/ONNXModelIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
165
Packages/com.unity.barracuda/Editor/ONNXModelIcon.png.meta
Normal file
165
Packages/com.unity.barracuda/Editor/ONNXModelIcon.png.meta
Normal file
@@ -0,0 +1,165 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44179f4142e33e24ca4feb8dfe55e56c
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 9
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -100
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: tvOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: PS4
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: Windows Store Apps
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: WebGL
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Packages/com.unity.barracuda/Editor/ONNXModelImporter.cs
Normal file
106
Packages/com.unity.barracuda/Editor/ONNXModelImporter.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.AssetImporters;
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#endif
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Barracuda.Editor;
|
||||
using Unity.Barracuda.ONNX;
|
||||
|
||||
[assembly: InternalsVisibleToAttribute("Barracuda.EditorTests")]
|
||||
[assembly: InternalsVisibleToAttribute("Unity.Barracuda.Tests")]
|
||||
|
||||
namespace Unity.Barracuda
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset Importer for Open Neural Network Exchange (ONNX) files.
|
||||
/// For more information about ONNX file format see: https://github.com/onnx/onnx
|
||||
/// </summary>
|
||||
[ScriptedImporter(34, new[] { "onnx" })]
|
||||
public class ONNXModelImporter : ScriptedImporter
|
||||
{
|
||||
// Configuration
|
||||
/// <summary>
|
||||
/// Enable ONNX model optimization during import. Set via importer UI
|
||||
/// </summary>
|
||||
public bool optimizeModel = true;
|
||||
|
||||
/// <summary>
|
||||
/// Fix batch size for ONNX models. Set via importer UI
|
||||
/// </summary>
|
||||
public bool forceArbitraryBatchSize = true;
|
||||
|
||||
/// <summary>
|
||||
/// Treat errors as warnings. Set via importer UI
|
||||
/// </summary>
|
||||
public bool treatErrorsAsWarnings = false;
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
internal ONNXModelConverter.ImportMode importMode = ONNXModelConverter.ImportMode.Standard;
|
||||
|
||||
[SerializeField, HideInInspector]
|
||||
internal ONNXModelConverter.DataTypeMode weightsTypeMode = ONNXModelConverter.DataTypeMode.Default;
|
||||
[SerializeField, HideInInspector]
|
||||
internal ONNXModelConverter.DataTypeMode activationTypeMode = ONNXModelConverter.DataTypeMode.Default;
|
||||
|
||||
internal const string iconName = "ONNXModelIcon";
|
||||
|
||||
|
||||
private Texture2D m_IconTexture;
|
||||
|
||||
/// <summary>
|
||||
/// Scripted importer callback
|
||||
/// </summary>
|
||||
/// <param name="ctx">Asset import context</param>
|
||||
public override void OnImportAsset(AssetImportContext ctx)
|
||||
{
|
||||
ONNXModelConverter.ModelImported += BarracudaAnalytics.SendBarracudaImportEvent;
|
||||
var converter = new ONNXModelConverter(optimizeModel, treatErrorsAsWarnings, forceArbitraryBatchSize, importMode);
|
||||
|
||||
var model = converter.Convert(ctx.assetPath);
|
||||
|
||||
if (weightsTypeMode == ONNXModelConverter.DataTypeMode.ForceHalf)
|
||||
model.ConvertWeights(DataType.Half);
|
||||
else if (weightsTypeMode == ONNXModelConverter.DataTypeMode.ForceFloat)
|
||||
model.ConvertWeights(DataType.Float);
|
||||
|
||||
NNModelData assetData = ScriptableObject.CreateInstance<NNModelData>();
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(memoryStream))
|
||||
{
|
||||
ModelWriter.Save(writer, model);
|
||||
assetData.Value = memoryStream.ToArray();
|
||||
}
|
||||
assetData.name = "Data";
|
||||
assetData.hideFlags = HideFlags.HideInHierarchy;
|
||||
|
||||
NNModel asset = ScriptableObject.CreateInstance<NNModel>();
|
||||
asset.modelData = assetData;
|
||||
|
||||
ctx.AddObjectToAsset("main obj", asset, LoadIconTexture());
|
||||
ctx.AddObjectToAsset("model data", assetData);
|
||||
|
||||
ctx.SetMainObject(asset);
|
||||
}
|
||||
|
||||
// Icon helper
|
||||
private Texture2D LoadIconTexture()
|
||||
{
|
||||
if (m_IconTexture == null)
|
||||
{
|
||||
string[] allCandidates = AssetDatabase.FindAssets(iconName);
|
||||
|
||||
if (allCandidates.Length > 0)
|
||||
{
|
||||
m_IconTexture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(allCandidates[0]), typeof(Texture2D)) as Texture2D;
|
||||
}
|
||||
}
|
||||
return m_IconTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 683b6cb6d0a474744822c888b46772c9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
461
Packages/com.unity.barracuda/Editor/ONNXModelImporterEditor.cs
Normal file
461
Packages/com.unity.barracuda/Editor/ONNXModelImporterEditor.cs
Normal file
@@ -0,0 +1,461 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
using UnityEditor.AssetImporters;
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#else
|
||||
using UnityEditor.Experimental.AssetImporters;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Unity.Barracuda.ONNX;
|
||||
using ImportMode=Unity.Barracuda.ONNX.ONNXModelConverter.ImportMode;
|
||||
using DataTypeMode=Unity.Barracuda.ONNX.ONNXModelConverter.DataTypeMode;
|
||||
|
||||
namespace Unity.Barracuda.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset Importer Editor of ONNX models
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ONNXModelImporter))]
|
||||
[CanEditMultipleObjects]
|
||||
public class ONNXModelImporterEditor : ScriptedImporterEditor
|
||||
{
|
||||
static PropertyInfo s_InspectorModeInfo;
|
||||
static ONNXModelImporterEditor()
|
||||
{
|
||||
s_InspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scripted importer editor UI callback
|
||||
/// </summary>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
var onnxModelImporter = target as ONNXModelImporter;
|
||||
if (onnxModelImporter == null)
|
||||
return;
|
||||
|
||||
InspectorMode inspectorMode = InspectorMode.Normal;
|
||||
if (s_InspectorModeInfo != null)
|
||||
inspectorMode = (InspectorMode)s_InspectorModeInfo.GetValue(assetSerializedObject);
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
bool debugView = inspectorMode != InspectorMode.Normal;
|
||||
SerializedProperty iterator = serializedObject.GetIterator();
|
||||
for (bool enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false)
|
||||
{
|
||||
if (iterator.propertyPath != "m_Script")
|
||||
EditorGUILayout.PropertyField(iterator, true);
|
||||
}
|
||||
|
||||
// Additional options exposed from ImportMode
|
||||
SerializedProperty importModeProperty = serializedObject.FindProperty(nameof(onnxModelImporter.importMode));
|
||||
bool skipMetadataImport = ((ImportMode)importModeProperty.intValue).HasFlag(ImportMode.SkipMetadataImport);
|
||||
if (EditorGUILayout.Toggle("Skip Metadata Import", skipMetadataImport) != skipMetadataImport)
|
||||
{
|
||||
importModeProperty.intValue ^= (int)ImportMode.SkipMetadataImport;
|
||||
}
|
||||
|
||||
if (debugView)
|
||||
{
|
||||
importModeProperty.intValue = (int)(ImportMode)EditorGUILayout.EnumFlagsField("Import Mode", (ImportMode)importModeProperty.intValue);
|
||||
|
||||
SerializedProperty weightsTypeMode = serializedObject.FindProperty(nameof(onnxModelImporter.weightsTypeMode));
|
||||
SerializedProperty activationTypeMode = serializedObject.FindProperty(nameof(onnxModelImporter.activationTypeMode));
|
||||
weightsTypeMode.intValue = (int)(DataTypeMode)EditorGUILayout.EnumPopup("Weights type", (DataTypeMode)weightsTypeMode.intValue);
|
||||
activationTypeMode.intValue = (int)(DataTypeMode)EditorGUILayout.EnumPopup("Activation type", (DataTypeMode)activationTypeMode.intValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (onnxModelImporter.optimizeModel)
|
||||
EditorGUILayout.HelpBox("Model optimizations are on\nRemove and re-import model if you observe incorrect behavior", MessageType.Info);
|
||||
|
||||
if (onnxModelImporter.importMode == ImportMode.Legacy)
|
||||
EditorGUILayout.HelpBox("Legacy importer is in use", MessageType.Warning);
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
ApplyRevertGUI();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asset Importer Editor of NNModel (the serialized file generated by ONNXModelImporter)
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(NNModel))]
|
||||
public class NNModelEditor : UnityEditor.Editor
|
||||
{
|
||||
// Use a static store for the foldouts, so it applies to all inspectors
|
||||
static Dictionary<string, bool> s_UIHelperFoldouts = new Dictionary<string, bool>();
|
||||
|
||||
private Model m_Model;
|
||||
private List<string> m_Inputs = new List<string>();
|
||||
private List<string> m_InputsDesc = new List<string>();
|
||||
private List<string> m_Outputs = new List<string>();
|
||||
private List<string> m_OutputsDesc = new List<string>();
|
||||
private List<string> m_Memories = new List<string>();
|
||||
private List<string> m_MemoriesDesc = new List<string>();
|
||||
private List<string> m_Layers = new List<string>();
|
||||
private List<string> m_LayersDesc = new List<string>();
|
||||
private List<string> m_Constants = new List<string>();
|
||||
private List<string> m_ConstantsDesc = new List<string>();
|
||||
|
||||
Dictionary<string, string> m_Metadata = new Dictionary<string, string>();
|
||||
Vector2 m_MetadataScrollPosition = Vector2.zero;
|
||||
// warnings
|
||||
private Dictionary<string, string> m_WarningsNeutral = new Dictionary<string, string>();
|
||||
private Dictionary<string, string> m_WarningsInfo = new Dictionary<string, string>();
|
||||
private Dictionary<string, string> m_WarningsWarning = new Dictionary<string, string>();
|
||||
private Dictionary<string, string> m_WarningsError = new Dictionary<string, string>();
|
||||
private Vector2 m_WarningsNeutralScrollPosition = Vector2.zero;
|
||||
private Vector2 m_WarningsInfoScrollPosition = Vector2.zero;
|
||||
private Vector2 m_WarningsWarningScrollPosition = Vector2.zero;
|
||||
private Vector2 m_WarningsErrorScrollPosition = Vector2.zero;
|
||||
|
||||
|
||||
private long m_NumEmbeddedWeights;
|
||||
private long m_NumConstantWeights;
|
||||
private long m_TotalWeightsSizeInBytes;
|
||||
|
||||
private Vector2 m_InputsScrollPosition = Vector2.zero;
|
||||
private Vector2 m_OutputsScrollPosition = Vector2.zero;
|
||||
private Vector2 m_MemoriesScrollPosition = Vector2.zero;
|
||||
private Vector2 m_LayerScrollPosition = Vector2.zero;
|
||||
private Vector2 m_ConstantScrollPosition = Vector2.zero;
|
||||
private const float k_Space = 5f;
|
||||
|
||||
private Texture2D m_IconTexture;
|
||||
private Texture2D LoadIconTexture()
|
||||
{
|
||||
if (m_IconTexture != null)
|
||||
return m_IconTexture;
|
||||
|
||||
string[] allCandidates = AssetDatabase.FindAssets(ONNXModelImporter.iconName);
|
||||
if (allCandidates.Length > 0)
|
||||
m_IconTexture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(allCandidates[0]), typeof(Texture2D)) as Texture2D;
|
||||
|
||||
return m_IconTexture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor static preview rendering callback
|
||||
/// </summary>
|
||||
/// <param name="assetPath">Asset path</param>
|
||||
/// <param name="subAssets">Child assets</param>
|
||||
/// <param name="width">width</param>
|
||||
/// <param name="height">height</param>
|
||||
/// <returns></returns>
|
||||
public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
|
||||
{
|
||||
Texture2D icon = LoadIconTexture();
|
||||
if (icon == null)
|
||||
return null;
|
||||
Texture2D tex = new Texture2D(width, height);
|
||||
EditorUtility.CopySerialized(icon, tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
private void AddDimension(StringBuilder stringBuilder, string name, int value, bool lastDim=false)
|
||||
{
|
||||
string strValue = (value >= 1) ? value.ToString() : "*";
|
||||
stringBuilder.AppendFormat("{0}:{1}", name, strValue);
|
||||
if (!lastDim)
|
||||
stringBuilder.Append(", ");
|
||||
}
|
||||
|
||||
private string GetUIStringFromShape(int[] shape)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder("shape: (", 50);
|
||||
if (shape.Length == 8)
|
||||
{
|
||||
bool is8D = (shape[0] > 1 || shape[1] > 1 || shape[3] > 1 || shape[4] > 1);
|
||||
if (is8D) AddDimension(stringBuilder, "s", shape[0]);
|
||||
if (is8D) AddDimension(stringBuilder, "r", shape[1]);
|
||||
AddDimension(stringBuilder, "n", shape[2]);
|
||||
if (is8D) AddDimension(stringBuilder, "t", shape[3]);
|
||||
if (is8D) AddDimension(stringBuilder, "d", shape[4]);
|
||||
AddDimension(stringBuilder, "h", shape[5]);
|
||||
AddDimension(stringBuilder, "w", shape[6]);
|
||||
AddDimension(stringBuilder, "c", shape[7], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.Assert(shape.Length == 4);
|
||||
AddDimension(stringBuilder, "n", shape[0]);
|
||||
AddDimension(stringBuilder, "h", shape[1]);
|
||||
AddDimension(stringBuilder, "w", shape[2]);
|
||||
AddDimension(stringBuilder, "c", shape[3], true);
|
||||
}
|
||||
stringBuilder.Append(")");
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
var nnModel = target as NNModel;
|
||||
if (nnModel == null)
|
||||
return;
|
||||
if (nnModel.modelData == null)
|
||||
return;
|
||||
|
||||
m_Model = nnModel.GetDeserializedModel();
|
||||
if (m_Model == null)
|
||||
return;
|
||||
|
||||
m_Inputs = m_Model.inputs.Select(i => i.name).ToList();
|
||||
m_InputsDesc = m_Model.inputs.Select(i => GetUIStringFromShape(i.shape)).ToList();
|
||||
m_Outputs = m_Model.outputs.ToList();
|
||||
|
||||
bool allKnownInputShapes = true;
|
||||
var inputShapes = new Dictionary<string, TensorShape>();
|
||||
foreach (var i in m_Model.inputs)
|
||||
{
|
||||
allKnownInputShapes = allKnownInputShapes && ModelAnalyzer.IsInputShapeAcceptablyKnowForShapeInference(i);
|
||||
if (!allKnownInputShapes)
|
||||
break;
|
||||
inputShapes.Add(i.name, new TensorShape(i.shape));
|
||||
}
|
||||
if (allKnownInputShapes)
|
||||
{
|
||||
m_OutputsDesc = m_Model.outputs.Select(i => {
|
||||
string output = "shape: (n:*, h:*, w:*, c:*)";
|
||||
try
|
||||
{
|
||||
TensorShape shape;
|
||||
if (ModelAnalyzer.TryGetOutputTensorShape(m_Model, inputShapes, i, out shape))
|
||||
output = GetUIStringFromShape(shape.ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Unexpected error while evaluating model output {i}. {e}");
|
||||
}
|
||||
return output; }).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_OutputsDesc = m_Model.outputs.Select(i => "shape: (n:*, h:*, w:*, c:*)").ToList();
|
||||
}
|
||||
|
||||
m_Memories = m_Model.memories.Select(i => i.input).ToList();
|
||||
m_MemoriesDesc = m_Model.memories.Select(i => $"shape:{i.shape.ToString()} output:{i.output}").ToList();
|
||||
|
||||
var layers = m_Model.layers.Where(i => i.type != Layer.Type.Load);
|
||||
var constants = m_Model.layers.Where(i => i.type == Layer.Type.Load);
|
||||
|
||||
m_Layers = layers.Select(i => i.type.ToString()).ToList();
|
||||
m_LayersDesc = layers.Select(i => i.ToString()).ToList();
|
||||
m_Constants = constants.Select(i => i.type.ToString()).ToList();
|
||||
m_ConstantsDesc = constants.Select(i => i.ToString()).ToList();
|
||||
|
||||
m_NumEmbeddedWeights = layers.Sum(l => (long)l.datasets.Sum(ds => (long)ds.length));
|
||||
m_NumConstantWeights = constants.Sum(l => (long)l.datasets.Sum(ds => (long)ds.length));
|
||||
|
||||
// weights are not loaded for UI, recompute size
|
||||
m_TotalWeightsSizeInBytes = 0;
|
||||
for (var l = 0; l < m_Model.layers.Count; ++l)
|
||||
for (var d = 0; d < m_Model.layers[l].datasets.Length; ++d)
|
||||
m_TotalWeightsSizeInBytes += m_Model.layers[l].datasets[d].length * m_Model.layers[l].datasets[d].itemSizeInBytes;
|
||||
|
||||
m_Metadata = new Dictionary<string, string>(m_Model.Metadata);
|
||||
|
||||
for (int i = 0; i < m_Model.Warnings.Count; i++)
|
||||
{
|
||||
var warning = m_Model.Warnings[i].LayerName;
|
||||
var warningDesc = m_Model.Warnings[i].Message;
|
||||
MessageType messageType = MessageType.Warning;
|
||||
if(warningDesc.StartsWith("MessageType"))
|
||||
{
|
||||
messageType = (MessageType)(warningDesc[12] - '0');
|
||||
warningDesc = warningDesc.Substring(13);
|
||||
}
|
||||
|
||||
switch (messageType)
|
||||
{
|
||||
case MessageType.None:
|
||||
m_WarningsNeutral[warning] = warningDesc;
|
||||
break;
|
||||
case MessageType.Info:
|
||||
m_WarningsInfo[warning] = warningDesc;
|
||||
break;
|
||||
case MessageType.Warning:
|
||||
m_WarningsWarning[warning] = warningDesc;
|
||||
break;
|
||||
case MessageType.Error:
|
||||
m_WarningsError[warning] = warningDesc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenNNModelAsTempFileButton(NNModel nnModel)
|
||||
{
|
||||
if (nnModel == null)
|
||||
return;
|
||||
if (nnModel.modelData == null)
|
||||
return;
|
||||
|
||||
if (GUILayout.Button("Open imported NN model as temp file"))
|
||||
{
|
||||
string tempPath = Application.temporaryCachePath;
|
||||
string filePath = Path.Combine(tempPath, nnModel.name);
|
||||
string filePathWithExtension = Path.ChangeExtension(filePath, "nn");
|
||||
File.WriteAllBytes(filePathWithExtension, nnModel.modelData.Value);
|
||||
System.Diagnostics.Process.Start(filePathWithExtension);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Editor UI rendering callback
|
||||
/// </summary>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (m_Model == null)
|
||||
return;
|
||||
|
||||
// HACK: When inspector settings are applied and the file is re-imported there doesn't seem to be a clean way to
|
||||
// get a notification from Unity, so we detect this change
|
||||
var nnModel = target as NNModel;
|
||||
if (nnModel && m_Model != nnModel.GetDeserializedModel())
|
||||
OnEnable(); // Model data changed underneath while inspector was active, so reload
|
||||
|
||||
GUI.enabled = true;
|
||||
OpenNNModelAsTempFileButton(nnModel);
|
||||
GUILayout.Label($"Source: {m_Model.IrSource}");
|
||||
GUILayout.Label($"Version: {m_Model.IrVersion}");
|
||||
GUILayout.Label($"Producer Name: {m_Model.ProducerName}");
|
||||
|
||||
if (m_Metadata.Any())
|
||||
{
|
||||
ListUIHelper($"Metadata {m_Metadata.Count}",
|
||||
m_Metadata.Keys.ToList(), m_Metadata.Values.ToList(), ref m_MetadataScrollPosition);
|
||||
}
|
||||
|
||||
if(m_WarningsError.Any())
|
||||
{
|
||||
ListUIHelper($"Errors {m_WarningsError.Count.ToString()}", m_WarningsError.Keys.ToList(), m_WarningsError.Values.ToList(), ref m_WarningsErrorScrollPosition);
|
||||
EditorGUILayout.HelpBox("Model contains errors. Behavior might be incorrect", MessageType.Error, true);
|
||||
}
|
||||
if(m_WarningsWarning.Any())
|
||||
{
|
||||
ListUIHelper($"Warnings {m_WarningsWarning.Count.ToString()}", m_WarningsWarning.Keys.ToList(), m_WarningsWarning.Values.ToList(), ref m_WarningsWarningScrollPosition);
|
||||
EditorGUILayout.HelpBox("Model contains warnings. Behavior might be incorrect", MessageType.Warning, true);
|
||||
}
|
||||
if(m_WarningsInfo.Any())
|
||||
{
|
||||
ListUIHelper($"Information: ", m_WarningsInfo.Keys.ToList(), m_WarningsInfo.Values.ToList(), ref m_WarningsInfoScrollPosition);
|
||||
EditorGUILayout.HelpBox("Model contains import information.", MessageType.Info, true);
|
||||
}
|
||||
if(m_WarningsNeutral.Any())
|
||||
{
|
||||
ListUIHelper($"Comments: ", m_WarningsNeutral.Keys.ToList(), m_WarningsNeutral.Values.ToList(), ref m_WarningsNeutralScrollPosition);
|
||||
}
|
||||
var constantWeightInfo = m_Constants.Count > 0 ? $" using {m_NumConstantWeights:n0} weights" : "";
|
||||
ListUIHelper($"Inputs ({m_Inputs.Count})", m_Inputs, m_InputsDesc, ref m_InputsScrollPosition);
|
||||
ListUIHelper($"Outputs ({m_Outputs.Count})", m_Outputs, m_OutputsDesc, ref m_OutputsScrollPosition);
|
||||
ListUIHelper($"Memories ({m_Memories.Count})", m_Memories, m_MemoriesDesc, ref m_MemoriesScrollPosition);
|
||||
ListUIHelper($"Layers ({m_Layers.Count} using {m_NumEmbeddedWeights:n0} embedded weights)", m_Layers, m_LayersDesc, ref m_LayerScrollPosition, m_Constants.Count == 0 ? 1.5f: 1f);
|
||||
ListUIHelper($"Constants ({m_Constants.Count}{constantWeightInfo})", m_Constants, m_ConstantsDesc, ref m_ConstantScrollPosition);
|
||||
|
||||
GUILayout.Label($"Total weight size: {m_TotalWeightsSizeInBytes:n0} bytes");
|
||||
}
|
||||
|
||||
private static void ListUIHelper(string sectionTitle, IReadOnlyList<string> names, IReadOnlyList<string> descriptions, ref Vector2 scrollPosition, float maxHeightMultiplier = 1f)
|
||||
{
|
||||
int n = names.Count();
|
||||
UnityEngine.Debug.Assert(descriptions.Count == n);
|
||||
if (descriptions.Count < n)
|
||||
return;
|
||||
|
||||
GUILayout.Space(k_Space);
|
||||
if (!s_UIHelperFoldouts.TryGetValue(sectionTitle, out bool foldout))
|
||||
foldout = true;
|
||||
|
||||
foldout = EditorGUILayout.Foldout(foldout, sectionTitle, true, EditorStyles.foldoutHeader);
|
||||
s_UIHelperFoldouts[sectionTitle] = foldout;
|
||||
if (foldout)
|
||||
{
|
||||
// GUILayout.Label(sectionTitle, EditorStyles.boldLabel);
|
||||
float height = Mathf.Min(n * 20f + 2f, 150f * maxHeightMultiplier);
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUI.skin.box, GUILayout.MinHeight(height));
|
||||
Event e = Event.current;
|
||||
float lineHeight = 16.0f;
|
||||
|
||||
StringBuilder fullText = new StringBuilder();
|
||||
fullText.Append(sectionTitle);
|
||||
fullText.AppendLine();
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
string name = names[i];
|
||||
string description = descriptions[i];
|
||||
fullText.Append($"{name} {description}");
|
||||
fullText.AppendLine();
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
Rect r = EditorGUILayout.GetControlRect(false, lineHeight);
|
||||
|
||||
string name = names[i];
|
||||
string description = descriptions[i];
|
||||
|
||||
// Context menu, "Copy"
|
||||
if (e.type == EventType.ContextClick && r.Contains(e.mousePosition))
|
||||
{
|
||||
e.Use();
|
||||
var menu = new GenericMenu();
|
||||
|
||||
// need to copy current value to be used in delegate
|
||||
// (C# closures close over variables, not their values)
|
||||
menu.AddItem(new GUIContent($"Copy current line"), false, delegate
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = $"{name} {description}";
|
||||
});
|
||||
menu.AddItem(new GUIContent($"Copy section"), false, delegate
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = fullText.ToString();
|
||||
});
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
// Color even line for readability
|
||||
if (e.type == EventType.Repaint)
|
||||
{
|
||||
GUIStyle st = "CN EntryBackEven";
|
||||
if ((i & 1) == 0)
|
||||
st.Draw(r, false, false, false, false);
|
||||
}
|
||||
|
||||
// layer name on the right side
|
||||
Rect locRect = r;
|
||||
locRect.xMax = locRect.xMin;
|
||||
GUIContent gc = new GUIContent(name.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
// calculate size so we can left-align it
|
||||
Vector2 size = EditorStyles.miniBoldLabel.CalcSize(gc);
|
||||
locRect.xMax += size.x;
|
||||
GUI.Label(locRect, gc, EditorStyles.miniBoldLabel);
|
||||
locRect.xMax += 2;
|
||||
|
||||
// message
|
||||
Rect msgRect = r;
|
||||
msgRect.xMin = locRect.xMax;
|
||||
GUI.Label(msgRect, new GUIContent(description.ToString(CultureInfo.InvariantCulture)), EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08ecb3218a86c6741aed5b2a299b203b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Unity.Barracuda.Editor",
|
||||
"references": [
|
||||
"Unity.Barracuda",
|
||||
"Unity.Barracuda.ONNX"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f1e7d835703842dda0e25142ed6c3c9
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user