This commit is contained in:
2024-09-20 20:30:10 +02:00
commit 4fabf1a6fd
29169 changed files with 1706941 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
{
"createSeparatePackage": false
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 278a7d565ddb1e840ac57ad1bb121e56
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,160 @@
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Rendering;
namespace UnityEngine.Rendering.Tests
{
class BitArrayTests
{
BitArray8[] ba8;
BitArray16[] ba16;
BitArray32[] ba32;
BitArray64[] ba64;
BitArray128[] ba128;
BitArray256[] ba256;
static readonly uint[] aIndexes = new uint[] { 300, 200, 198, 100, 98, 60, 58, 30, 28, 10, 8, 4, 2, 0, 0 }; //double 0 entry to test double entry
static readonly uint[] bIndexes = new uint[] { 300, 200, 199, 100, 99, 60, 59, 30, 29, 10, 9, 8, 5, 1, 0 };
static readonly uint[] getSetTestedIndexes = new uint[] { 201, 200, 101, 100, 61, 60, 31, 30, 11, 10, 1, 0 }; // on a, odd value are false, even true
const string aHumanized = "00000000.00000000.00000000.00000000.00000000.00000000.00000001.01000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00010100.00000000.00000000.00000000.00000000.00010100.00000000.00000000.00000000.01010000.00000000.00000101.00010101";
const string bHumanized = "00000000.00000000.00000000.00000000.00000000.00000000.00000001.10000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00011000.00000000.00000000.00000000.00000000.00011000.00000000.00000000.00000000.01100000.00000000.00000111.00100011";
const string aAndBHumanized = "00000000.00000000.00000000.00000000.00000000.00000000.00000001.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00010000.00000000.00000000.00000000.00000000.00010000.00000000.00000000.00000000.01000000.00000000.00000101.00000001";
const string aOrBHumanized = "00000000.00000000.00000000.00000000.00000000.00000000.00000001.11000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00011100.00000000.00000000.00000000.00000000.00011100.00000000.00000000.00000000.01110000.00000000.00000111.00110111";
const string notAHumanized = "11111111.11111111.11111111.11111111.11111111.11111111.11111110.10111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11101011.11111111.11111111.11111111.11111111.11101011.11111111.11111111.11111111.10101111.11111111.11111010.11101010";
const string zeroHumanized = "00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000.00000000";
const string maxHumanized = "11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111.11111111";
[SetUp]
public void SetUpBitArray()
{
ba8 = new[] { new BitArray8(), new BitArray8(aIndexes), new BitArray8(bIndexes), new BitArray8(byte.MaxValue) };
ba16 = new[] { new BitArray16(), new BitArray16(aIndexes), new BitArray16(bIndexes), new BitArray16(ushort.MaxValue) };
ba32 = new[] { new BitArray32(), new BitArray32(aIndexes), new BitArray32(bIndexes), new BitArray32(uint.MaxValue) };
ba64 = new[] { new BitArray64(), new BitArray64(aIndexes), new BitArray64(bIndexes), new BitArray64(ulong.MaxValue) };
ba128 = new[] { new BitArray128(), new BitArray128(aIndexes), new BitArray128(bIndexes), new BitArray128(ulong.MaxValue, ulong.MaxValue) };
ba256 = new[] { new BitArray256(), new BitArray256(aIndexes), new BitArray256(bIndexes), new BitArray256(ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue) };
}
//[TearDown]
//nothing to do as they are non static struct
string GetLastHumanizedBits(string a, uint bitNumber) => a.Substring(a.Length - ((int)bitNumber + ((int)bitNumber - 1) / 8)); //handle '.' separators
void TestBitArrayMethods<T>(T[] ba, uint capacity)
where T : IBitArray
{
Assert.AreEqual(capacity, ba[0].capacity);
Assert.AreEqual(true, ba[0].allFalse);
Assert.AreEqual(false, ba[0].allTrue);
var trimmedZeroHumanized = GetLastHumanizedBits(zeroHumanized, capacity);
var humanized = ba[0].humanizedData;
Assert.AreEqual(trimmedZeroHumanized, humanized);
Assert.AreEqual(capacity, ba[1].capacity);
Assert.AreEqual(false, ba[1].allFalse);
Assert.AreEqual(false, ba[1].allTrue);
var trimmedAHumanized = GetLastHumanizedBits(aHumanized, capacity);
humanized = ba[1].humanizedData;
Assert.AreEqual(trimmedAHumanized, humanized);
Assert.AreEqual(capacity, ba[2].capacity);
Assert.AreEqual(false, ba[2].allFalse);
Assert.AreEqual(false, ba[2].allTrue);
var trimmedBHumanized = GetLastHumanizedBits(bHumanized, capacity);
humanized = ba[2].humanizedData;
Assert.AreEqual(trimmedBHumanized, humanized);
Assert.AreEqual(capacity, ba[3].capacity);
Assert.AreEqual(false, ba[3].allFalse);
Assert.AreEqual(true, ba[3].allTrue);
var trimmedMaxHumnized = GetLastHumanizedBits(maxHumanized, capacity);
humanized = ba[3].humanizedData;
Assert.AreEqual(trimmedMaxHumnized, humanized);
}
void TestBitArrayOperator<T>(T[] ba)
where T : IBitArray
{
//ensure we keep value type when refactoring
var ba_4 = ba[1];
Assert.AreEqual(ba_4, ba[1]);
Assert.AreNotSame(ba_4, ba[1]);
ba_4 = ba[2];
Assert.AreNotEqual(ba_4, ba[1]);
//test and
var bAndA = ba[2].BitAnd(ba[1]);
var aAndB = ba[1].BitAnd(ba[2]);
Assert.AreEqual(bAndA, aAndB);
Assert.AreEqual(bAndA.humanizedData, GetLastHumanizedBits(aAndBHumanized, ba[0].capacity));
//test or
var bOrA = ba[2].BitOr(ba[1]);
var aOrB = ba[1].BitOr(ba[2]);
Assert.AreEqual(bOrA, aOrB);
Assert.AreEqual(bOrA.humanizedData, GetLastHumanizedBits(aOrBHumanized, ba[0].capacity));
//test not
var notA = ba[1].BitNot();
Assert.AreEqual(notA.humanizedData, GetLastHumanizedBits(notAHumanized, ba[0].capacity));
//test indexer
foreach (uint index in getSetTestedIndexes.Where(i => i < ba[0].capacity))
{
//test get
Assert.AreEqual(ba[1][index], (index & 1) == 0); //on a, odd value are false and even true
//test set
var bai = ba[1];
bai[index] = ba[1][index];
Assert.AreEqual(ba[1], bai);
bai[index] = !ba[1][index];
Assert.AreNotEqual(ba[1], bai);
Assert.AreEqual(bai[index], !ba[1][index]);
}
}
[Test]
public void TestBitArray8()
{
TestBitArrayMethods(ba8, 8u);
TestBitArrayOperator(ba8);
}
[Test]
public void TestBitArray16()
{
TestBitArrayMethods(ba16, 16u);
TestBitArrayOperator(ba16);
}
[Test]
public void TestBitArray32()
{
TestBitArrayMethods(ba32, 32u);
TestBitArrayOperator(ba32);
}
[Test]
public void TestBitArray64()
{
TestBitArrayMethods(ba64, 64u);
TestBitArrayOperator(ba64);
}
[Test]
public void TestBitArray128()
{
TestBitArrayMethods(ba128, 128u);
TestBitArrayOperator(ba128);
}
[Test]
public void TestBitArray256()
{
TestBitArrayMethods(ba256, 256u);
TestBitArrayOperator(ba256);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d6a697ae64dee440a8f3b0f3894cdae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
using NUnit.Framework;
namespace UnityEngine.Rendering
{
public class CoreRenderPipelinePreferencesTests
{
[Test]
public void RegisterInvalidPreferenceColorName()
{
bool RegisterColor(string colorName)
{
try
{
CoreRenderPipelinePreferences.RegisterPreferenceColor(colorName, Color.black);
}
catch
{
return false;
}
return true;
}
Assert.False(RegisterColor(null));
Assert.False(RegisterColor(""));
}
[Test]
public void RegisterPreferenceColor()
{
var color = CoreRenderPipelinePreferences.RegisterPreferenceColor("TEST/DEFAULT_GREEN", Color.green);
Assert.True(color() == Color.green);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bba6d5f6892ce2244a2fd45d3050dfd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,226 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using Unity.Collections;
namespace UnityEditor.Rendering.Tests
{
unsafe class CoreUnsafeUtilsTests
{
public struct TestData : IEquatable<TestData>
{
public int intValue;
public float floatValue;
public bool Equals(TestData other)
{
return intValue == other.intValue && floatValue == other.floatValue;
}
public override bool Equals(object obj)
{
if (!(obj is TestData))
return false;
return Equals((TestData)obj);
}
public override int GetHashCode()
{
fixed (float* fptr = &floatValue)
return intValue ^ *(int*)fptr;
}
}
static object[][] s_CopyToList = new object[][]
{
new object[] { new List<TestData>
{
new TestData { floatValue = 2, intValue = 1 },
new TestData { floatValue = 3, intValue = 2 },
new TestData { floatValue = 4, intValue = 3 },
new TestData { floatValue = 5, intValue = 4 },
new TestData { floatValue = 6, intValue = 5 },
} }
};
[Test]
[TestCaseSource("s_CopyToList")]
public void CopyToList(List<TestData> datas)
{
var dest = stackalloc TestData[datas.Count];
datas.CopyTo(dest, datas.Count);
for (int i = 0; i < datas.Count; ++i)
Assert.AreEqual(datas[i], dest[i]);
}
static object[][] s_CopyToArray = new object[][]
{
new object[] { new TestData[]
{
new TestData { floatValue = 2, intValue = 1 },
new TestData { floatValue = 3, intValue = 2 },
new TestData { floatValue = 4, intValue = 3 },
new TestData { floatValue = 5, intValue = 4 },
new TestData { floatValue = 6, intValue = 5 },
} }
};
[Test]
[TestCaseSource("s_CopyToArray")]
public void CopyToArray(TestData[] datas)
{
var dest = stackalloc TestData[datas.Length];
datas.CopyTo(dest, datas.Length);
for (int i = 0; i < datas.Length; ++i)
Assert.AreEqual(datas[i], dest[i]);
}
static object[][] s_QuickSort = new object[][]
{
new object[] { new int[] { 0, 1 } },
new object[] { new int[] { 1, 0 } },
new object[] { new int[] { 0, 4, 2, 6, 3, 7, 1, 5 } }, // Test with unique set
new object[] { new int[] { 0, 4, 2, 6, 4, 7, 1, 5 } }, // Test with non unique set
};
[Test]
[TestCaseSource("s_QuickSort")]
public void QuickSort(int[] values)
{
// We must perform a copy to avoid messing the test data directly
var ptrValues = stackalloc int[values.Length];
values.CopyTo(ptrValues, values.Length);
CoreUnsafeUtils.QuickSort<int>(values.Length, ptrValues);
for (int i = 0; i < values.Length - 1; ++i)
Assert.LessOrEqual(ptrValues[i], ptrValues[i + 1]);
}
static object[][] s_QuickSortHash = new object[][]
{
new object[]
{
new Hash128[] { Hash128.Parse("78b27b84a9011b5403e836b9dfa51e33"), Hash128.Parse("c7417d322c083197631326bccf3f9ea0"), Hash128.Parse("dd27f0dc4ffe20b0f8ecc0e4fdf618fe") },
new Hash128[] { Hash128.Parse("dd27f0dc4ffe20b0f8ecc0e4fdf618fe"), Hash128.Parse("c7417d322c083197631326bccf3f9ea0"), Hash128.Parse("78b27b84a9011b5403e836b9dfa51e33") },
},
};
[Test]
[TestCaseSource("s_QuickSortHash")]
public void QuickSortHash(Hash128[] l, Hash128[] r)
{
var lPtr = stackalloc Hash128[l.Length];
var rPtr = stackalloc Hash128[r.Length];
for (int i = 0; i < l.Length; ++i)
{
lPtr[i] = l[i];
rPtr[i] = r[i];
}
CoreUnsafeUtils.QuickSort<Hash128>(l.Length, lPtr);
CoreUnsafeUtils.QuickSort<Hash128>(r.Length, rPtr);
for (int i = 0; i < l.Length - 1; ++i)
{
Assert.LessOrEqual(lPtr[i], lPtr[i + 1]);
Assert.LessOrEqual(rPtr[i], rPtr[i + 1]);
}
for (int i = 0; i < l.Length; ++i)
{
Assert.AreEqual(lPtr[i], rPtr[i]);
}
}
static object[][] s_UintSortData = new object[][]
{
new object[] { new uint[] { 0 } },
new object[] { new uint[] { 0, 1, 20123, 29, 0xffffff } },
new object[] { new uint[] { 0xff1235, 92, 22125, 67358, 92123, 7012, 1234, 10000 } }, // Test with unique set
};
[Test]
[TestCaseSource("s_UintSortData")]
public void InsertionSort(uint[] values)
{
var array = new NativeArray<uint>(values, Allocator.Temp);
CoreUnsafeUtils.InsertionSort(array, array.Length);
for (int i = 0; i < array.Length - 1; ++i)
Assert.LessOrEqual(array[i], array[i + 1]);
array.Dispose();
}
[Test]
[TestCaseSource("s_UintSortData")]
public void MergeSort(uint[] values)
{
NativeArray<uint> supportArray = new NativeArray<uint>();
var array = new NativeArray<uint>(values, Allocator.Temp);
CoreUnsafeUtils.MergeSort(array, array.Length, ref supportArray);
for (int i = 0; i < array.Length - 1; ++i)
Assert.LessOrEqual(array[i], array[i + 1]);
array.Dispose();
supportArray.Dispose();
}
[Test]
[TestCaseSource("s_UintSortData")]
public void RadixSort(uint[] values)
{
NativeArray<uint> supportArray = new NativeArray<uint>();
var array = new NativeArray<uint>(values, Allocator.Temp);
CoreUnsafeUtils.RadixSort(array, array.Length, ref supportArray);
for (int i = 0; i < array.Length - 1; ++i)
Assert.LessOrEqual(array[i], array[i + 1]);
array.Dispose();
supportArray.Dispose();
}
static object[][] s_PartialSortData = new object[][]
{
new object[] { new uint[] { 2, 8, 9, 2, 4, 0, 1, 0, 1, 0 } }
};
private enum SortAlgorithm
{
Insertion,
Merge,
Radix
};
[Test]
[TestCaseSource("s_PartialSortData")]
public void PartialSortInsertionMergeRadix(uint[] values)
{
NativeArray<uint> supportArray = new NativeArray<uint>();
int sortCount = 5;
foreach (var algorithmId in Enum.GetValues(typeof(SortAlgorithm)))
{
var algorithmValue = (SortAlgorithm)algorithmId;
var array = new NativeArray<uint>(values, Allocator.Temp);
if (algorithmValue == SortAlgorithm.Insertion)
CoreUnsafeUtils.InsertionSort(array, sortCount);
else if (algorithmValue == SortAlgorithm.Merge)
CoreUnsafeUtils.MergeSort(array, sortCount, ref supportArray);
else if (algorithmValue == SortAlgorithm.Radix)
CoreUnsafeUtils.RadixSort(array, sortCount, ref supportArray);
for (int i = 0; i < sortCount - 1; ++i)
Assert.LessOrEqual(array[i], array[i + 1]);
for (int i = sortCount; i < array.Length; ++i)
Assert.That(array[i] == 0 || array[i] == 1);
array.Dispose();
}
supportArray.Dispose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cd9e56dab7f922843a012c6d4bc3f1d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b8f37e7cf15bcc1419f4622045f6e075
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using NUnit.Framework;
using System.Linq;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Tests
{
partial class RenderingDebuggerTests
{
private DebugUI.Panel m_Panel;
[SetUp]
public void Setup()
{
m_Panel = DebugManager.instance.GetPanel("Tests", true);
}
[TearDown]
public void TearDown()
{
DebugManager.instance.RemovePanel(m_Panel);
}
static TestCaseData[] s_TestCaseDatasGetItem =
{
new TestCaseData(new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed }, DebugUI.Flags.FrequentlyUsed)
.SetName("Given a widget with a flag, when looking for items with that flag, then the item is returned")
.Returns(new string[] { "element" }),
new TestCaseData(new DebugUI.BoolField() { displayName = "element" }, DebugUI.Flags.FrequentlyUsed)
.SetName("Given a widget without flags, when looking for items with a flag, then nothing is returned")
.Returns(new string[] { }),
new TestCaseData(new DebugUI.Foldout()
{
displayName = "foldout",
flags = DebugUI.Flags.FrequentlyUsed,
children = { new DebugUI.BoolField() { displayName = "element" } }
}, DebugUI.Flags.FrequentlyUsed)
.SetName("Given a container widget with children and a flag, when looking for items with a flag, the container is returned")
.Returns(new string[] { "foldout" }),
new TestCaseData(new DebugUI.Foldout()
{
displayName = "foldout",
flags = DebugUI.Flags.FrequentlyUsed,
children = { new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed, } }
}, DebugUI.Flags.FrequentlyUsed)
.SetName("Given a container and children with a flag, when looking for a flag, then only the container is returned")
.Returns(new string[] { "foldout" }),
new TestCaseData(new DebugUI.Foldout()
{
displayName = "foldout",
children = { new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed, }, new DebugUI.BoolField() { displayName = "element2", flags = DebugUI.Flags.FrequentlyUsed, } }
}, DebugUI.Flags.FrequentlyUsed)
.SetName("Given multiple children widgets with a flag, when looking for a flag the item is returned")
.Returns(new string[] { "element", "element2" }),
};
[Test, TestCaseSource(nameof(s_TestCaseDatasGetItem))]
public string[] GetItemTestFlags(DebugUI.Widget widget, DebugUI.Flags flags)
{
m_Panel.children.Add(widget);
var itemsFoundNames = DebugManager.instance
.GetItemsFromContainer(flags, m_Panel)
.Select(i => i.displayName).ToArray();
return itemsFoundNames;
}
static TestCaseData[] s_TestCaseDatasGetItemQueryPath =
{
new TestCaseData(new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed }, "Tests -> element")
.SetName("Given a widget, when looking by it's query path, then the item is found")
.Returns("element"),
new TestCaseData(new DebugUI.BoolField() { displayName = "element" }, "Tests -> element2")
.SetName("Given a query path that does not map to any widget, when looking for it, nothing is found")
.Returns(string.Empty),
new TestCaseData(new DebugUI.Foldout()
{
displayName = "foldout",
flags = DebugUI.Flags.FrequentlyUsed,
children = { new DebugUI.BoolField() { displayName = "element" } }
}, "Tests -> foldout -> element")
.SetName("Given a query path for a child widget, when using that query path, then the child object is returned")
.Returns("element"),
};
[Test, TestCaseSource(nameof(s_TestCaseDatasGetItemQueryPath))]
public string GetItemTestQueryPath(DebugUI.Widget widget, string queryPath)
{
m_Panel.children.Add(widget);
var itemsFoundNames = DebugManager.instance
.GetItem(queryPath)?
.displayName ?? string.Empty;
return itemsFoundNames;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 910d5b0536f6ed44a8ce8618000af3db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
using NUnit.Framework;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Tests
{
partial class RenderingDebuggerTests
{
/// <summary>
/// Class for the rendering debugger settings.
/// </summary>
public class TestDebugDisplaySettings : DebugDisplaySettings<TestDebugDisplaySettings>
{
public abstract class TestDebugDisplaySettingsData : IDebugDisplaySettingsData
{
#region IDebugDisplaySettingsData
/// <inheritdoc/>
public bool AreAnySettingsActive => false;
/// <inheritdoc/>
public bool IsPostProcessingAllowed => true;
/// <inheritdoc/>
public bool IsLightingActive => true;
public abstract IDebugDisplaySettingsPanelDisposable CreatePanel();
public bool TryGetScreenClearColor(ref UnityEngine.Color _) => false;
#endregion
}
class Test1Panel : TestDebugDisplaySettingsData
{
[DisplayInfo(name = "Test 1", order = 1)]
private class StatsPanel : DebugDisplaySettingsPanel
{
public override DebugUI.Flags Flags => DebugUI.Flags.RuntimeOnly;
public StatsPanel()
{
AddWidget(new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed });
}
}
/// <inheritdoc/>
public override IDebugDisplaySettingsPanelDisposable CreatePanel()
{
return new StatsPanel();
}
}
class Test2Panel : TestDebugDisplaySettingsData
{
[DisplayInfo(name = "Test 2", order = 2)]
private class StatsPanel : DebugDisplaySettingsPanel
{
public override DebugUI.Flags Flags => DebugUI.Flags.RuntimeOnly;
public StatsPanel()
{
AddWidget(new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed });
}
}
/// <inheritdoc/>
public override IDebugDisplaySettingsPanelDisposable CreatePanel()
{
return new StatsPanel();
}
}
class Test3Panel : TestDebugDisplaySettingsData
{
[DisplayInfo(name = "Test 3", order = 3)]
private class StatsPanel : DebugDisplaySettingsPanel
{
public override DebugUI.Flags Flags => DebugUI.Flags.RuntimeOnly;
public StatsPanel()
{
AddWidget(new DebugUI.BoolField() { displayName = "element", flags = DebugUI.Flags.FrequentlyUsed });
}
}
/// <inheritdoc/>
public override IDebugDisplaySettingsPanelDisposable CreatePanel()
{
return new StatsPanel();
}
}
public TestDebugDisplaySettings()
{
Reset();
}
/// <inheritdoc/>
public override void Reset()
{
m_Settings.Clear();
// Add them in an unsorted way
Add(new Test3Panel());
Add(new Test1Panel());
Add(new Test2Panel());
}
}
[Test]
public void TestOrderAndPanelName()
{
var debugDisplaySettingsUI = new DebugDisplaySettingsUI();
debugDisplaySettingsUI.RegisterDebug(TestDebugDisplaySettings.Instance);
var panelTest1Index = DebugManager.instance.PanelIndex("Test 1");
Assert.IsTrue(panelTest1Index >= 0);
var panelTest2Index = DebugManager.instance.PanelIndex("Test 2");
Assert.IsTrue(panelTest2Index >= 0);
var panelTest3Index = DebugManager.instance.PanelIndex("Test 3");
Assert.IsTrue(panelTest3Index >= 0);
Assert.True(panelTest1Index < panelTest2Index);
Assert.True(panelTest2Index < panelTest3Index);
debugDisplaySettingsUI.UnregisterDebug();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d751686736fade3409bf8547030c42a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
using NUnit.Framework;
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Tests
{
partial class RenderingDebuggerTests
{
private static void PerformUndoRedoGeneric<TDebugState, T>(UnityEngine.Rendering.DebugUI.IValueField field, T defaultValue, T valueToSet)
where TDebugState : DebugState<T>, new()
{
DebugState<T> state = ScriptableObject.CreateInstance<TDebugState>();
state.SetValue(defaultValue, field);
Undo.RecordObject(state, nameof(PerformUndoRedoGeneric));
state.SetValue(valueToSet, field);
Undo.PerformUndo();
Assert.AreEqual(defaultValue, state.value);
Undo.PerformRedo();
Assert.AreEqual(valueToSet, state.value);
}
public class BitFieldTests<T> : UnityEngine.Rendering.DebugUI.BitField
{
public BitFieldTests()
{
enumType = typeof(T);
}
}
public class AutoEnumFieldTest : UnityEngine.Rendering.DebugUI.EnumField
{
public AutoEnumFieldTest()
{
autoEnum = typeof(LightType);
}
}
public class PathEnumFieldTest : UnityEngine.Rendering.DebugUI.EnumField
{
public PathEnumFieldTest()
{
enumNames = new GUIContent[] { new GUIContent("Item1"), new GUIContent("Catergory/Item1"), new GUIContent("Catergory/Item2") };
enumValues = new int[] { 0, 2, 4 };
}
}
static TestCaseData[] s_TestCaseDatas =
{
new TestCaseData(typeof(DebugStateEnum), typeof(UnityEngine.Rendering.DebugUI.EnumField), typeof(int), 0, 2)
.SetName("Undo/Redo works for Enums"),
new TestCaseData(typeof(DebugStateEnum), typeof(AutoEnumFieldTest), typeof(int), LightType.Disc, LightType.Spot)
.SetName("Undo/Redo works for Auto Enums"),
new TestCaseData(typeof(DebugStateEnum), typeof(PathEnumFieldTest), typeof(int), 0, 4)
.SetName("Undo/Redo works for Path Enums"),
new TestCaseData(typeof(DebugStateBool), typeof(UnityEngine.Rendering.DebugUI.BoolField), typeof(bool), false, true)
.SetName("Undo/Redo works for Booleans"),
new TestCaseData(typeof(DebugStateColor), typeof(UnityEngine.Rendering.DebugUI.ColorField), typeof(Color), Color.green, Color.red)
.SetName("Undo/Redo works for Colors"),
new TestCaseData(typeof(DebugStateFloat), typeof(UnityEngine.Rendering.DebugUI.FloatField), typeof(float), 1.0f, 5.0f)
.SetName("Undo/Redo works for Floats"),
new TestCaseData(typeof(DebugStateInt), typeof(UnityEngine.Rendering.DebugUI.IntField), typeof(int), -1, 5)
.SetName("Undo/Redo works for integers"),
new TestCaseData(typeof(DebugStateUInt), typeof(UnityEngine.Rendering.DebugUI.UIntField), typeof(uint), 1u, 5u)
.SetName("Undo/Redo works for unsigned integers"),
new TestCaseData(typeof(DebugStateVector2), typeof(UnityEngine.Rendering.DebugUI.Vector2Field), typeof(Vector2), Vector2.zero, Vector2.up)
.SetName("Undo/Redo works for vector 2"),
new TestCaseData(typeof(DebugStateVector3), typeof(UnityEngine.Rendering.DebugUI.Vector3Field), typeof(Vector3), Vector3.zero, Vector3.up)
.SetName("Undo/Redo works for vector 3"),
new TestCaseData(typeof(DebugStateVector4), typeof(UnityEngine.Rendering.DebugUI.Vector4Field), typeof(Vector4), Vector4.zero, Vector4.one)
.SetName("Undo/Redo works for vector 4"),
new TestCaseData(typeof(DebugStateFlags), typeof(BitFieldTests<UnityEngine.Rendering.DebugUI.Flags>), typeof(Enum), UnityEngine.Rendering.DebugUI.Flags.EditorOnly, UnityEngine.Rendering.DebugUI.Flags.RuntimeOnly)
.SetName("Undo/Redo works for bit fields"),
};
[Test, TestCaseSource(nameof(s_TestCaseDatas))]
public void PerformUndoRedo(Type debugStateType, Type widgetType, Type innerType, object defaultValue, object changeValue)
{
var widget = Activator.CreateInstance(widgetType);
var performUndoRedoGeneric = GetType()
.GetMethod(nameof(PerformUndoRedoGeneric), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
.MakeGenericMethod(new[] { debugStateType, innerType } );
object[] args = new object[] { widget, defaultValue, changeValue };
performUndoRedoGeneric.Invoke(null, args);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b2e284253ba6e14785f3a5aacdfcdc5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,497 @@
using System;
using NUnit.Framework;
using UnityEngine.Profiling;
namespace UnityEngine.Rendering.Tests
{
class DynamicArrayTests
{
DynamicArray<int> m_DynamicArray;
[SetUp]
public void Setup()
{
m_DynamicArray = new DynamicArray<int>();
}
[OneTimeTearDown]
public void CleanUp()
{
m_DynamicArray = null;
}
[Test]
public void TestContainElement()
{
m_DynamicArray.Add(2);
Assert.IsTrue(m_DynamicArray.Contains(2));
Assert.IsFalse(m_DynamicArray.Contains(55));
}
[Test]
public void TestAddElementCorrectSize()
{
Assert.AreEqual(0, m_DynamicArray.size);
m_DynamicArray.Add(2);
Assert.AreEqual(1, m_DynamicArray.size);
m_DynamicArray.Add(4);
Assert.AreEqual(2, m_DynamicArray.size);
}
[Test]
public void TestAddRangeCorrectElements()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
var otherArray = new DynamicArray<int>();
otherArray.Add(3);
otherArray.Add(4);
m_DynamicArray.AddRange(otherArray);
Assert.AreEqual(1, m_DynamicArray[0]);
Assert.AreEqual(2, m_DynamicArray[1]);
Assert.AreEqual(3, m_DynamicArray[2]);
Assert.AreEqual(4, m_DynamicArray[3]);
Assert.AreEqual(4, m_DynamicArray.size);
}
[Test]
public void TestAddRangeOutOfSpaceKeepPreviousElements()
{
var smallDynamicArray = new DynamicArray<int>(2);
smallDynamicArray[0] = 1;
smallDynamicArray[1] = 2;
var otherArray = new DynamicArray<int>();
otherArray.Add(3);
otherArray.Add(4);
smallDynamicArray.AddRange(otherArray);
Assert.AreEqual(1, smallDynamicArray[0]);
Assert.AreEqual(2, smallDynamicArray[1]);
Assert.AreEqual(3, smallDynamicArray[2]);
Assert.AreEqual(4, smallDynamicArray[3]);
Assert.AreEqual(4, smallDynamicArray.size);
}
[Test]
public void TestRemoveElementCorrectSize()
{
m_DynamicArray.Add(2);
m_DynamicArray.Add(4);
Assert.AreEqual(2, m_DynamicArray.size);
m_DynamicArray.Remove(2);
Assert.AreEqual(1, m_DynamicArray.size);
m_DynamicArray.Remove(4);
Assert.AreEqual(0, m_DynamicArray.size);
}
[Test]
public void TestRemoveAtKeepOrder()
{
m_DynamicArray.Add(2);
m_DynamicArray.Add(4);
m_DynamicArray.Add(8);
Assert.AreEqual(3, m_DynamicArray.size);
m_DynamicArray.RemoveAt(1);
Assert.AreEqual(2, m_DynamicArray.size);
Assert.AreEqual(2, m_DynamicArray[0]);
Assert.AreEqual(8, m_DynamicArray[1]);
}
[Test]
public void TestRemoveInexistantElementReturnFalse()
{
m_DynamicArray.Add(2);
m_DynamicArray.Add(4);
Assert.IsFalse(m_DynamicArray.Remove(8));
Assert.AreEqual(2, m_DynamicArray.size);
}
[Test]
public void TestRemoveKeepOrder()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
m_DynamicArray.Add(3);
m_DynamicArray.Add(4);
Assert.AreEqual(1, m_DynamicArray[0]);
Assert.AreEqual(2, m_DynamicArray[1]);
Assert.AreEqual(3, m_DynamicArray[2]);
Assert.AreEqual(4, m_DynamicArray[3]);
Assert.AreEqual(4, m_DynamicArray.size);
m_DynamicArray.Remove(2);
Assert.IsFalse(m_DynamicArray.Contains(2));
Assert.AreEqual(1, m_DynamicArray[0]);
Assert.AreEqual(3, m_DynamicArray[1]);
Assert.AreEqual(4, m_DynamicArray[2]);
Assert.AreEqual(3, m_DynamicArray.size);
}
[Test]
public void TestRemoveRangeKeepOrder()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
m_DynamicArray.Add(3);
m_DynamicArray.Add(4);
m_DynamicArray.Add(5);
Assert.AreEqual(1, m_DynamicArray[0]);
Assert.AreEqual(2, m_DynamicArray[1]);
Assert.AreEqual(3, m_DynamicArray[2]);
Assert.AreEqual(4, m_DynamicArray[3]);
Assert.AreEqual(5, m_DynamicArray[4]);
Assert.AreEqual(5, m_DynamicArray.size);
m_DynamicArray.RemoveRange(1, 2);
Assert.IsFalse(m_DynamicArray.Contains(2));
Assert.IsFalse(m_DynamicArray.Contains(3));
Assert.AreEqual(1, m_DynamicArray[0]);
Assert.AreEqual(4, m_DynamicArray[1]);
Assert.AreEqual(5, m_DynamicArray[2]);
Assert.AreEqual(3, m_DynamicArray.size);
}
[Test]
public void TestInvalidAccessThrows()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
int dummy;
Assert.Throws<System.IndexOutOfRangeException>(() => dummy = m_DynamicArray[2]);
Assert.Throws<System.IndexOutOfRangeException>(() => dummy = m_DynamicArray[-1]);
}
[Test]
public void TestRemoveAtInvalidAccessThrows()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
Assert.Throws<System.IndexOutOfRangeException>(() => m_DynamicArray.RemoveAt(-1));
Assert.Throws<System.IndexOutOfRangeException>(() => m_DynamicArray.RemoveAt(2));
}
[Test]
public void TestRemoveRangeInvalidAccessThrows()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
Assert.Throws<System.ArgumentOutOfRangeException>(() => m_DynamicArray.RemoveRange(1, 2));
}
[Test]
public void TestRemoveRangeValidAccessDoesNotThrows()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
Assert.DoesNotThrow(() => m_DynamicArray.RemoveRange(1, 1));
}
[Test]
public void TestFindIndexFailReturnMinusOne()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
Assert.AreEqual(-1, m_DynamicArray.FindIndex(0, m_DynamicArray.size, (x) => x == 4));
}
[Test]
public void TestFindIndexSuccessReturnRightFirstIndex()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
m_DynamicArray.Add(3);
m_DynamicArray.Add(2);
Assert.AreEqual(1, m_DynamicArray.FindIndex(0, m_DynamicArray.size, (x) => x == 2));
}
[Test]
public void TestIndexOfReturnFirstValidIndex()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
m_DynamicArray.Add(3);
m_DynamicArray.Add(2);
Assert.AreEqual(1, m_DynamicArray.IndexOf(2));
}
[Test]
public void TestIndexOfRangeRespectCount()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(2);
m_DynamicArray.Add(3);
m_DynamicArray.Add(4);
Assert.AreEqual(-1, m_DynamicArray.IndexOf(4, 1, 2));
}
[Test]
public void TestIndexOfRangeReturnFirstValidIndex()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(4);
m_DynamicArray.Add(3);
m_DynamicArray.Add(4);
m_DynamicArray.Add(5);
Assert.AreEqual(1, m_DynamicArray.IndexOf(4, 1, 3));
}
[Test]
public void TestIndexOfWithStartingIndexFail()
{
m_DynamicArray.Add(1);
m_DynamicArray.Add(4);
m_DynamicArray.Add(3);
Assert.AreEqual(-1, m_DynamicArray.IndexOf(1, 1, 2));
}
[Test]
public void TestResizeIncreaseCapacity()
{
m_DynamicArray.Resize(256);
Assert.AreEqual(256, m_DynamicArray.capacity);
}
[Test]
public void TestReserveKeepsSize()
{
var size = m_DynamicArray.size;
m_DynamicArray.Reserve(256);
Assert.AreEqual(256, m_DynamicArray.capacity);
Assert.AreEqual(size, m_DynamicArray.size);
}
[Test]
public void TestQuickSort()
{
m_DynamicArray.Add(8);
m_DynamicArray.Add(4);
m_DynamicArray.Add(3);
m_DynamicArray.Add(4);
m_DynamicArray.Add(5);
m_DynamicArray.Add(1);
m_DynamicArray.Add(12);
m_DynamicArray.QuickSort();
Assert.AreEqual(1, m_DynamicArray[0]);
Assert.AreEqual(3, m_DynamicArray[1]);
Assert.AreEqual(4, m_DynamicArray[2]);
Assert.AreEqual(4, m_DynamicArray[3]);
Assert.AreEqual(5, m_DynamicArray[4]);
Assert.AreEqual(8, m_DynamicArray[5]);
Assert.AreEqual(12, m_DynamicArray[6]);
}
[Test]
public void TestForEach_FullRange()
{
m_DynamicArray.Add(8);
m_DynamicArray.Add(4);
m_DynamicArray.Add(3);
m_DynamicArray.Add(6);
var sum = 0;
foreach (var i in m_DynamicArray)
{
sum += i;
}
Assert.AreEqual(21, sum);
// now check if we are _not_ iterating on empty array
sum = 0;
m_DynamicArray.Clear();
foreach (var i in m_DynamicArray)
{
sum += i;
}
Assert.AreEqual(0, sum);
}
[Test]
public void TestForEach_SubRange()
{
m_DynamicArray.Add(8);
m_DynamicArray.Add(4);
m_DynamicArray.Add(3);
m_DynamicArray.Add(6);
var sum = 0;
foreach (var i in m_DynamicArray.SubRange(1,2))
{
sum += i;
}
Assert.AreEqual(7, sum);
//remove item of array and test again
m_DynamicArray.RemoveAt(1);
sum = 0;
foreach (var i in m_DynamicArray.SubRange(1,2))
{
sum += i;
}
Assert.AreEqual(9, sum);
}
[Test]
public void GetEnumerators_ArgumentValidation()
{
Assert.Throws<ArgumentNullException>(() => {
var iterator = new DynamicArray<int>.Iterator(null);
});
Assert.Throws<ArgumentNullException>(() => {
var iterator = new DynamicArray<int>.RangeEnumerable.RangeIterator(null, 0,0);
});
m_DynamicArray.Add(12);
m_DynamicArray.Add(2);
Assert.Throws<IndexOutOfRangeException>(() => { var iterator = m_DynamicArray.SubRange(-5, 1); });
Assert.Throws<IndexOutOfRangeException>(() => { var iterator = m_DynamicArray.SubRange(17, 1); });
Assert.Throws<IndexOutOfRangeException>(() => { var iterator = m_DynamicArray.SubRange(0, 12); });
Assert.Throws<IndexOutOfRangeException>(() => { var iterator = m_DynamicArray.SubRange(1, 2); });
Assert.DoesNotThrow(() => { var iterator = m_DynamicArray.SubRange(1, 1); });
Assert.Throws<IndexOutOfRangeException>(() => { var iterator = m_DynamicArray.SubRange(2, 1); });
}
[Test]
public void Foreach_TestNoModificationsAllowed()
{
Assert.Throws<InvalidOperationException>(() =>
{
m_DynamicArray.Add(3);
m_DynamicArray.Add(3);
foreach (var i in m_DynamicArray)
{
m_DynamicArray.Remove(i);
}
});
Assert.Throws<InvalidOperationException>(() =>
{
m_DynamicArray.Add(3);
m_DynamicArray.Add(3);
foreach (var i in m_DynamicArray)
{
m_DynamicArray.Add(1);
}
});
Assert.Throws<InvalidOperationException>(() =>
{
m_DynamicArray.Add(8);
m_DynamicArray.Add(3);
foreach (var i in m_DynamicArray)
{
if (i == 3)
{
m_DynamicArray.Add(1);
m_DynamicArray.RemoveAt(0);
}
}
});
Assert.Throws<InvalidOperationException>(() =>
{
m_DynamicArray.Add(8);
m_DynamicArray.Add(3);
m_DynamicArray.Add(7);
foreach (var i in m_DynamicArray)
{
if (i == 3)
{
m_DynamicArray.QuickSort();
}
}
});
}
static Recorder gcAllocRecorder = Recorder.Get("GC.Alloc");
static int CountGCAllocs(Action action)
{
gcAllocRecorder.FilterToCurrentThread();
gcAllocRecorder.enabled = false;
gcAllocRecorder.enabled = true;
action();
gcAllocRecorder.enabled = false;
return gcAllocRecorder.sampleBlockCount;
}
static void ValidateNoGCAllocs(Action action)
{
// warmup
// this will catch static c'tors etc
CountGCAllocs(action);
// actual test
var count = CountGCAllocs(action);
if (count != 0)
throw new AssertionException($"Expected 0 GC allocations but there were {count}");
}
[Test]
public void Foreach_NoGC()
{
m_DynamicArray.Reserve(4);
m_DynamicArray.Add(12);
m_DynamicArray.Add(4);
m_DynamicArray.Add(8);
m_DynamicArray.Add(2);
var sum = 0;
ValidateNoGCAllocs(() =>
{
sum = 0;
foreach (var i in m_DynamicArray)
{
sum += i;
}
});
Assert.AreEqual(sum, 26);
ValidateNoGCAllocs(() =>
{
sum = 0;
foreach (var i in m_DynamicArray.SubRange(0, 2))
{
sum += i;
}
});
Assert.AreEqual(sum, 16);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c0441c3d2890d0489e50062a48fb582
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
class EditorExampleTest
{
[Test]
public void EditorSampleTestSimplePasses()
{
// Use the Assert class to test conditions.
}
// A UnityTest behaves like a coroutine in PlayMode
// and allows you to yield null to skip a frame in EditMode
[UnityTest]
public IEnumerator EditorSampleTestWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// yield to skip a frame
yield return null;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87e6389ce577ca74aa755fb446fe8ca0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using NUnit.Framework;
using System;
namespace UnityEngine.Rendering.Tests
{
unsafe class FixedBufferStringQueueTests
{
[Test]
public void PushAndPopInBufferRange()
{
const int size = 512;
var bufferStart = stackalloc byte[size];
var buffer = new CoreUnsafeUtils.FixedBufferStringQueue(bufferStart, size);
Assert.True(buffer.TryPush("Lorem ipsum dolor sit"));
Assert.True(buffer.TryPush("amet, consectetur adipiscing"));
Assert.True(buffer.TryPush("elit, sed do eiusmod"));
Assert.True(buffer.TryPush("tempor incididunt ut labore"));
Assert.AreEqual(4, buffer.Count);
Assert.True(buffer.TryPop(out string v) && v == "Lorem ipsum dolor sit");
Assert.True(buffer.TryPop(out v) && v == "amet, consectetur adipiscing");
Assert.True(buffer.TryPop(out v) && v == "elit, sed do eiusmod");
Assert.True(buffer.TryPop(out v) && v == "tempor incididunt ut labore");
}
[Test]
public void PushAndPopOutOfBufferRange()
{
const int size = 64;
var bufferStart = stackalloc byte[size];
var buffer = new CoreUnsafeUtils.FixedBufferStringQueue(bufferStart, size);
Assert.True(buffer.TryPush("Lorem ipsum dolor sit"));
Assert.False(buffer.TryPush("amet, consectetur adipiscing"));
Assert.AreEqual(1, buffer.Count);
}
[Test]
public void PushAndPopAndClear()
{
const int size = 128;
var bufferStart = stackalloc byte[size];
var buffer = new CoreUnsafeUtils.FixedBufferStringQueue(bufferStart, size);
Assert.True(buffer.TryPush("Lorem ipsum dolor sit"));
Assert.True(buffer.TryPush("amet, consectetur adipiscing"));
Assert.False(buffer.TryPush("elit, sed do eiusmod"));
Assert.AreEqual(2, buffer.Count);
buffer.Clear();
Assert.AreEqual(0, buffer.Count);
Assert.True(buffer.TryPush("elit, sed do eiusmod"));
Assert.True(buffer.TryPush("tempor incididunt ut labore"));
Assert.True(buffer.TryPop(out string v) && v == "elit, sed do eiusmod");
Assert.True(buffer.TryPop(out v) && v == "tempor incididunt ut labore");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e49a53fbd01acd4c9254d29a48f13d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace UnityEngine.Rendering.Tests
{
class InputRegisteringTests
{
static TestCaseData[] s_DuplicateTestCaseDatas =
{
new TestCaseData(new List<InputManagerEntry>()
{
new() { name = "Another Action 1", kind = InputManagerEntry.Kind.Axis },
new() { name = "Duplicate Action", kind = InputManagerEntry.Kind.Axis },
new() { name = "Another Action 2", kind = InputManagerEntry.Kind.Axis },
new() { name = "Duplicate Action", kind = InputManagerEntry.Kind.Axis },
new() { name = "Duplicate Action", kind = InputManagerEntry.Kind.Axis },
})
.SetName("Given a set containing duplicate entries, when removing duplicate entries, only the first of each duplicate is kept")
.Returns(new string[] { "Another Action 1", "Duplicate Action", "Another Action 2" }),
new TestCaseData(new List<InputManagerEntry>()
{
new() { name = "Duplicate Action", kind = InputManagerEntry.Kind.Axis },
new() { name = "Duplicate Action", kind = InputManagerEntry.Kind.Mouse },
})
.SetName("Given entries with duplicate names but different kinds, when removing duplicate entries, both are kept")
.Returns(new string[] { "Duplicate Action", "Duplicate Action" }),
};
[Test, TestCaseSource(nameof(s_DuplicateTestCaseDatas))]
public string[] GetEntriesWithoutDuplicates(List<InputManagerEntry> entries)
{
return InputRegistering.GetEntriesWithoutDuplicates(entries)
.Select(e => e.name).ToArray();
}
static TestCaseData[] s_AlreadyRegisteredTestCaseDatas =
{
new TestCaseData(new List<InputManagerEntry>()
{
new() { name = "New Action 1", kind = InputManagerEntry.Kind.Axis },
new() { name = "New Action 2", kind = InputManagerEntry.Kind.Mouse },
new() { name = "Old Action 1", kind = InputManagerEntry.Kind.Axis },
new() { name = "Old Action 2", kind = InputManagerEntry.Kind.Mouse },
}, new List<(string, InputManagerEntry.Kind)>()
{
("Old Action 1", InputManagerEntry.Kind.Axis),
("Old Action 2", InputManagerEntry.Kind.Mouse),
("Old Action 3", InputManagerEntry.Kind.Mouse)
})
.SetName("Given a set of entries where some of them is already registered, when filtering out already registered entries, they are removed")
.Returns(new string[] { "New Action 1", "New Action 2" }),
new TestCaseData(new List<InputManagerEntry>()
{
new() { name = "Old Action 1", kind = InputManagerEntry.Kind.Axis },
new() { name = "Old Action 2", kind = InputManagerEntry.Kind.Mouse },
}, new List<(string, InputManagerEntry.Kind)>()
{
("Old Action 1", InputManagerEntry.Kind.Axis),
("Old Action 2", InputManagerEntry.Kind.Axis),
})
.SetName("Given an entry with already registered name but different kind, when filtering out already registered entries, only ones with matching kind are removed")
.Returns(new string[] { "Old Action 2" }),
};
[Test, TestCaseSource(nameof(s_AlreadyRegisteredTestCaseDatas))]
public string[] GetEntriesWithoutAlreadyRegistered(List<InputManagerEntry> entries, List<(string, InputManagerEntry.Kind)> cachedEntries)
{
return InputRegistering.GetEntriesWithoutAlreadyRegistered(entries, cachedEntries)
.Select(e => e.name).ToArray();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd586e92a1d94555961c28634174b450
timeCreated: 1660290864

View File

@@ -0,0 +1,110 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace UnityEngine.Rendering.Tests
{
public static class ReflectionUtils
{
/// <summary>
/// Finds a type by full name
/// </summary>
/// <param name="name">The full type name with namespace</param>
/// <returns>The found type</returns>
public static Type FindTypeByName(string name)
{
var type = AppDomain.CurrentDomain
.GetAssemblies()
.Select(assembly => assembly.GetType(name))
.FirstOrDefault(tt => tt != null);
Assert.True(type != null, "Type not found");
return type;
}
/// <summary>
/// Calls a private method from a class
/// </summary>
/// <param name="methodName">The method name</param>
/// <param name="args">The arguments to pass to the method</param>
public static object InvokeStatic(this Type targetType, string methodName, params object[] args)
{
Assert.True(targetType != null, "Invalid Type");
Assert.IsNotEmpty(methodName, "The methodName to set could not be null");
var mi = targetType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
Assert.True(mi != null, $"Could not find method `{methodName}` on type `{targetType}`");
return mi.Invoke(null, args);
}
/// <summary>
/// Calls a private method from a class
/// </summary>
/// <param name="methodName">The method name</param>
/// <param name="args">The arguments to pass to the method</param>
public static object Invoke(this object target, string methodName, params object[] args)
{
Assert.True(target != null, "The target could not be null");
Assert.IsNotEmpty(methodName, "The method name to set could not be null");
var mi = target.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
Assert.True(mi != null, $"Could not find method `{methodName}` on object `{target}`");
return mi.Invoke(target, args);
}
private static FieldInfo FindField(this Type type, string fieldName)
{
FieldInfo fi = null;
while (type != null)
{
fi = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (fi != null) break;
type = type.BaseType;
}
Assert.True(fi != null, $"Could not find method `{fieldName}` on object `{type}`");
return fi;
}
/// <summary>
/// Sets a private field from a class
/// </summary>
/// <param name="fieldName">The field to change</param>
/// <param name="value">The new value</param>
public static void SetField(this object target, string fieldName, object value)
{
Assert.True(target != null, "The target could not be null");
Assert.IsNotEmpty(fieldName, "The field to set could not be null");
target.GetType().FindField(fieldName).SetValue(target, value);
}
/// <summary>
/// Gets the value of a private field from a class
/// </summary>
/// <param name="fieldName">The field to get</param>
public static object GetField(this object target, string fieldName)
{
Assert.True(target != null, "The target could not be null");
Assert.IsNotEmpty(fieldName, "The field to set could not be null");
return target.GetType().FindField(fieldName).GetValue(target);
}
/// <summary>
/// Gets all the fields from a class
/// </summary>
public static IEnumerable<FieldInfo> GetFields(this object target)
{
Assert.True(target != null, "The target could not be null");
return target.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.OrderBy(t => t.MetadataToken);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 994ab27378e49eb45830d39c4c0b5799
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,180 @@
using NUnit.Framework;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using UnityEditor;
using UnityEditor.Rendering;
namespace UnityEngine.Rendering.Tests
{
class RemoveComponent
{
#region Components
interface ITest { }
protected class Apple : MonoBehaviour, ITest { }
protected class Banana : MonoBehaviour, ITest { }
[RequireComponent(typeof(Apple))]
protected class AdditionalApple : MonoBehaviour, IAdditionalData, ITest { }
[RequireComponent(typeof(Banana))]
protected class AdditionalBanana : MonoBehaviour, IAdditionalData, ITest { }
[RequireComponent(typeof(Banana))]
protected class AdditionalBananaColor : MonoBehaviour, IAdditionalData, ITest { }
protected class WaterMelon : MonoBehaviour, IAdditionalData, ITest{ }
[RequireComponent(typeof(Apple), typeof(Banana))]
protected class FruitBasket : MonoBehaviour, IAdditionalData, ITest { }
#endregion
#region SetUp & TearDown
protected GameObject m_GameObject;
[SetUp]
public void SetUp()
{
m_GameObject = new("RemoveComponentTestsRoot");
}
[TearDown]
public void TearDown()
{
GameObject.DestroyImmediate(m_GameObject);
}
#endregion
protected Type[] GenericRemoveComponent(
[DisallowNull] int selectionAmount,
[DisallowNull] Type componentToRemove,
[DisallowNull] Type[] componentsToAdd,
[DisallowNull] Action<Component> removeMethod)
{
Assert.IsTrue(selectionAmount > 0, "GenericRemoveComponent should only be called with a non null selection");
//Create object to select
GameObject[] selectedGameObjects = new GameObject[selectionAmount];
for (int i = 0; i < selectionAmount; ++i)
{
(selectedGameObjects[i] = new GameObject($"Selected_{i}", componentsToAdd)).transform.parent = m_GameObject.transform;
}
//update selected objects
Selection.objects = selectedGameObjects;
//Call method to test
removeMethod(Selection.activeGameObject.GetComponent(componentToRemove));
//Assert all selection have same component
var typesOfFirstSelected = selectedGameObjects[0].GetComponents<ITest>().Select(c => c.GetType());
Assert.IsTrue(DoesAllGameObjectHaveSameComponents(selectedGameObjects.Skip(1), typesOfFirstSelected), "Not all the GameObject of the selection have same Components");
//Will assert that remaining components are the desired ones
return typesOfFirstSelected.ToArray();
}
bool DoesAllGameObjectHaveSameComponents([NotNull] System.Collections.Generic.IEnumerable<GameObject> objectsToCheck, System.Collections.Generic.IEnumerable<Type> typesToCheck)
{
var typeAmount = typesToCheck.Count();
foreach(var objectToCheck in objectsToCheck)
{
if (objectToCheck.GetComponents<ITest>().Length != typeAmount)
return false;
foreach (var typeToCheck in typesToCheck)
if (!objectToCheck.TryGetComponent(typeToCheck, out _))
return false;
}
return true;
}
}
[TestOf(typeof(RemoveComponentUtils))]
class RemoveComponentUtilsTests : RemoveComponent
{
//No multiedition test needed as this is not Selection dependant
static TestCaseData[] s_RemoveComponentTestCaseDatas =
{
new TestCaseData(typeof(Banana), new Type[] {typeof(Banana), typeof(AdditionalBanana)})
.SetName("Removal of target component removes it's additional data")
.Returns(Array.Empty<Type>()),
new TestCaseData(typeof(Banana), new Type[] {typeof(Banana), typeof(AdditionalBanana), typeof(AdditionalBananaColor)})
.SetName("Removal of target component removes all it's additional datas")
.Returns(Array.Empty<Type>()),
new TestCaseData(typeof(Apple), new Type[] {typeof(Banana), typeof(AdditionalBanana), typeof(Apple), typeof(AdditionalApple)})
.SetName("Given multiple components, each with additional datas, the removal of the component only removes its additional datas")
.Returns(new Type[] {typeof(Banana), typeof(AdditionalBanana) }),
};
[Test, TestCaseSource(nameof(s_RemoveComponentTestCaseDatas))]
public Type[] RemoveComponentAndPropagateTheDeleteToAdditionalDatas([DisallowNull] Type componentToRemove, [DisallowNull] Type[] componentsToAdd)
{
return GenericRemoveComponent(1, componentToRemove, componentsToAdd, RemoveComponentUtils.RemoveComponent);
}
}
[TestOf(typeof(RemoveAdditionalDataUtils))]
class RemoveAdditionalDataUtilsTests : RemoveComponent
{
//No multiedition test needed as this is not Selection dependant
static TestCaseData[] s_TryGetComponentsToRemoveTestCaseDatas =
{
new TestCaseData(typeof(AdditionalBanana))
.Returns(new string[] {"Banana"})
.SetName("For additional data targeting one component, return the targeted component (most common case)"),
new TestCaseData(typeof(FruitBasket))
.Returns(new string[] { "Apple", "Banana" })
.SetName("For additional data targeting multiple components, return all the targeted components"),
new TestCaseData(typeof(WaterMelon))
.Returns(Array.Empty<string>())
.SetName("For additional data targetting no component, return empty collection."),
};
[Test, TestCaseSource(nameof(s_TryGetComponentsToRemoveTestCaseDatas))]
public string[] TryGetComponentsToRemove([DisallowNull] Type type)
{
string[] result = Array.Empty<string>();
var additionalData = m_GameObject.AddComponent(type) as IAdditionalData;
using (ListPool<Type>.Get(out var componentsToRemove))
{
if (RemoveAdditionalDataUtils.TryGetComponentsToRemove(additionalData, componentsToRemove, out var error))
result = componentsToRemove.Select(t => t.Name).ToArray();
}
return result;
}
//No multiedition RECQUIRED: This is Selection dependent to handle correctly the popup
static TestCaseData[] s_RemoveAdditionalDataComponentTestCaseDatas =
{
new TestCaseData(1, typeof(AdditionalBanana), new Type[] {typeof(Banana), typeof(AdditionalBanana) })
.SetName("For single additional data, when removing it, the target component is deleted")
.Returns(Array.Empty<Type>()),
new TestCaseData(1, typeof(AdditionalBananaColor), new Type[] {typeof(Banana), typeof(AdditionalBanana), typeof(AdditionalBananaColor)})
.SetName("For multiple additional datas, when removing one of them, target component is deleted, and the other additional data")
.Returns(Array.Empty<Type>()),
new TestCaseData(1, typeof(AdditionalBananaColor), new Type[] {typeof(Banana), typeof(Banana), typeof(AdditionalBanana), typeof(AdditionalBananaColor)})
.SetName("For multiple additional component and datas, when removing one of them everything is removed")
.Returns(Array.Empty<Type>()),
new TestCaseData(1, typeof(AdditionalApple), new Type[] {typeof(Banana), typeof(AdditionalBanana), typeof(Apple), typeof(AdditionalApple)})
.SetName("For multiple types of target component, when deleting an additional data, only the target component is being removed")
.Returns(new Type[] {typeof(Banana), typeof(AdditionalBanana)}),
new TestCaseData(3, typeof(AdditionalBanana), new Type[] {typeof(Banana), typeof(AdditionalBanana) })
.SetName("For single additional data, when removing it, the target component is deleted (multiedition case)")
.Returns(Array.Empty<Type>()),
new TestCaseData(3, typeof(AdditionalBananaColor), new Type[] {typeof(Banana), typeof(AdditionalBanana), typeof(AdditionalBananaColor)})
.SetName("For multiple additional datas, when removing one of them, target component is deleted, and the other additional data (multiedition case)")
.Returns(Array.Empty<Type>()),
new TestCaseData(3, typeof(AdditionalBananaColor), new Type[] {typeof(Banana), typeof(Banana), typeof(AdditionalBanana), typeof(AdditionalBananaColor)})
.SetName("For multiple additional component and datas, when removing one of them everything is removed (multiedition case)")
.Returns(Array.Empty<Type>()),
new TestCaseData(3, typeof(AdditionalApple), new Type[] {typeof(Banana), typeof(AdditionalBanana), typeof(Apple), typeof(AdditionalApple)})
.SetName("For multiple types of target component, when deleting an additional data, only the target component is being removed (multiedition case)")
.Returns(new Type[] {typeof(Banana), typeof(AdditionalBanana)})
};
[Test, TestCaseSource(nameof(s_RemoveAdditionalDataComponentTestCaseDatas))]
[NUnit.Framework.Property("FogBugz", "1396805")]
[NUnit.Framework.Property("Jira", "UUM-5452")]
public Type[] RemoveAdditionalDataComponentAndPropagateToComponent(int selectionAmount, [DisallowNull] Type componentToRemove, [DisallowNull] Type[] componentsToAdd)
{
return GenericRemoveComponent(selectionAmount, componentToRemove, componentsToAdd, c => RemoveAdditionalDataUtils.RemoveAdditionalData(new UnityEditor.MenuCommand(c), false));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b39e85330fc80e43b645b014ca68eda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Tests
{
public class RemoveRangeExtensionsTests
{
static TestCaseData[] s_ListTestsCaseDatas =
{
new TestCaseData(new int[] {1,2,3,4,5,6}, 1, 2).SetName("Remove middle"),
new TestCaseData(new int[] {1,2,3,4,5,6}, 0, 2).SetName("Remove front"),
new TestCaseData(new int[] {1,2,3,4,5,6}, 0, 6).SetName("Remove all"),
new TestCaseData(new int[] {1,2,3,4,5,6}, 5, 1).SetName("Remove back"),
new TestCaseData(new int[] {1,2,3,4,5,6}, 0, 0).SetName("Index 0"),
new TestCaseData(new int[] {1,2,3,4,5,6}, 5, 0).SetName("Count 0")
};
bool ItemInRangeAreRemovedAfterRemoveRange<TList>(TList list, int startIndex, int count)
where TList : IList<int>
{
using (ListPool<int>.Get(out var copy))
{
foreach (int integer in list)
copy.Add(integer);
if (list.TryRemoveElementsInRange(startIndex, count, out var exception))
{
copy.RemoveRange(startIndex, count);
return copy.SequenceEqual(list);
}
return false;
}
}
[Test, TestCaseSource(nameof(s_ListTestsCaseDatas))]
public void ItemInRangeAreRemovedAfterRemoveRangeForList(int[] ints, int startIndex, int count)
{
using (GenericPool<SimpleList>.Get(out var copy))
{
copy.AddRange(ints);
Assert.IsTrue(ItemInRangeAreRemovedAfterRemoveRange<IList<int>>(copy as IList<int>, startIndex, count));
}
}
[Test, TestCaseSource(nameof(s_ListTestsCaseDatas))]
public void ItemInRangeAreRemovedAfterRemoveRangeForSimpleList(int[] ints, int startIndex, int count)
{
using (ListPool<int>.Get(out var copy))
{
copy.AddRange(ints);
Assert.IsTrue(ItemInRangeAreRemovedAfterRemoveRange<List<int>>(copy, startIndex, count));
}
}
static TestCaseData[] s_ListTestsCaseDatasExceptions =
{
new TestCaseData(new int[] {1,2,3,4,5,6}, 5, -1).SetName("Count negative").Returns(typeof(ArgumentOutOfRangeException)),
new TestCaseData(new int[] {1,2,3,4,5,6}, -1, 2).SetName("Index negative").Returns(typeof(ArgumentOutOfRangeException)),
new TestCaseData(new int[] {1,2,3,4,5,6}, 5, 5).SetName("Count exceeds list size").Returns(typeof(ArgumentException)),
};
Exception ExceptionsAreCorrect<TList>(TList list, int startIndex, int count)
where TList : IList<int>
{
list.TryRemoveElementsInRange(startIndex, count, out var error);
return error;
}
[Test, TestCaseSource(nameof(s_ListTestsCaseDatasExceptions))]
public Type ExceptionsAreCorrectForList(int[] ints, int startIndex, int count)
{
using (ListPool<int>.Get(out var copy))
{
copy.AddRange(ints);
return ExceptionsAreCorrect(copy, startIndex, count).GetType();
}
}
[Test, TestCaseSource(nameof(s_ListTestsCaseDatasExceptions))]
public Type ExceptionsAreCorrectForSimpleList(int[] ints, int startIndex, int count)
{
using (GenericPool<SimpleList>.Get(out var copy))
{
copy.AddRange(ints);
return ExceptionsAreCorrect(copy, startIndex, count).GetType();
}
}
class SimpleList : IList<int>
{
private List<int> m_List = new List<int>();
public void AddRange(int[] ints)
{
m_List.Clear();
m_List.AddRange(ints);
}
public IEnumerator<int> GetEnumerator()
{
return m_List.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(int item)
{
m_List.Add(item);
}
public void Clear()
{
m_List.Clear();
}
public bool Contains(int item)
{
return m_List.Contains(item);
}
public void CopyTo(int[] array, int arrayIndex)
{
m_List.CopyTo(array, arrayIndex);
}
public bool Remove(int item)
{
return m_List.Remove(item);
}
public int Count => m_List.Count;
public bool IsReadOnly => false;
public int IndexOf(int item)
{
return m_List.IndexOf(item);
}
public void Insert(int index, int item)
{
m_List.Insert(index, item);
}
public void RemoveAt(int index)
{
m_List.RemoveAt(index);
}
public int this[int index]
{
get => m_List[index];
set => m_List[index] = value;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37ec18a486a522e4d8e28946e685e59d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,490 @@
using NUnit.Framework;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Tests
{
class RenderGraphTests
{
RenderGraph m_RenderGraph = new RenderGraph();
[SetUp]
public void SetupRenderGraph()
{
m_RenderGraph.ClearCompiledGraph();
}
[OneTimeTearDown]
public void CleanUp()
{
m_RenderGraph.Cleanup();
}
class RenderGraphTestPassData
{
public TextureHandle[] textures = new TextureHandle[8];
public ComputeBufferHandle[] buffers = new ComputeBufferHandle[8];
}
// Final output (back buffer) of render graph needs to be explicitly imported in order to know that the chain of dependency should not be culled.
[Test]
public void WriteToBackBufferNotCulled()
{
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(1, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[0].culled);
}
// If no back buffer is ever written to, everything should be culled.
[Test]
public void NoWriteToBackBufferCulled()
{
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(1, compiledPasses.size);
Assert.AreEqual(true, compiledPasses[0].culled);
}
// Writing to imported resource is considered as a side effect so passes should not be culled.
[Test]
public void WriteToImportedTextureNotCulled()
{
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
builder.WriteTexture(m_RenderGraph.ImportTexture(null));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(1, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[0].culled);
}
[Test]
public void WriteToImportedComputeBufferNotCulled()
{
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
builder.WriteComputeBuffer(m_RenderGraph.ImportComputeBuffer(null));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(1, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[0].culled);
}
[Test]
public void PassWriteResourcePartialNotReadAfterNotCulled()
{
// If a pass writes to a resource that is not unused globally by the graph but not read ever AFTER the pass then the pass should be culled unless it writes to another used resource.
TextureHandle texture0;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
TextureHandle texture1;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
builder.ReadTexture(texture0);
texture1 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// This pass writes to texture0 which is used so will not be culled out.
// Since texture0 is never read after this pass, we should decrement refCount for this pass and potentially cull it.
// However, it also writes to texture1 which is used in the last pass so we mustn't cull it.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData))
{
builder.WriteTexture(texture0);
builder.WriteTexture(texture1);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass3", out var passData))
{
builder.ReadTexture(texture1);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(4, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[0].culled);
Assert.AreEqual(false, compiledPasses[1].culled);
Assert.AreEqual(false, compiledPasses[2].culled);
Assert.AreEqual(false, compiledPasses[3].culled);
}
[Test]
public void PassDisallowCullingNotCulled()
{
// This pass does nothing so should be culled but we explicitly disallow it.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
builder.AllowPassCulling(false);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(1, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[0].culled);
}
// First pass produces two textures and second pass only read one of the two. Pass one should not be culled.
[Test]
public void PartialUnusedProductNotCulled()
{
TextureHandle texture;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
builder.ReadTexture(texture);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(2, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[0].culled);
Assert.AreEqual(false, compiledPasses[1].culled);
}
// Simple cycle of create/release of a texture across multiple passes.
[Test]
public void SimpleCreateReleaseTexture()
{
TextureHandle texture;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Add dummy passes
for (int i = 0; i < 2; ++i)
{
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData))
{
builder.ReadTexture(texture);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(4, compiledPasses.size);
Assert.Contains(texture.handle.index, compiledPasses[0].resourceCreateList[(int)RenderGraphResourceType.Texture]);
Assert.Contains(texture.handle.index, compiledPasses[3].resourceReleaseList[(int)RenderGraphResourceType.Texture]);
}
[Test]
public void UseTransientOutsidePassRaiseException()
{
Assert.Catch<System.ArgumentException>(() =>
{
TextureHandle texture;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture = builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm });
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
builder.ReadTexture(texture); // This is illegal (transient resource was created in previous pass)
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
});
}
[Test]
public void TransientCreateReleaseInSamePass()
{
TextureHandle texture;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture = builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm });
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(1, compiledPasses.size);
Assert.Contains(texture.handle.index, compiledPasses[0].resourceCreateList[(int)RenderGraphResourceType.Texture]);
Assert.Contains(texture.handle.index, compiledPasses[0].resourceReleaseList[(int)RenderGraphResourceType.Texture]);
}
// Texture that should be released during an async pass should have their release delayed until the first pass that syncs with the compute pipe.
// Otherwise they may be reused by the graphics pipe even if the async pipe is not done executing.
[Test]
public void AsyncPassReleaseTextureOnGraphicsPipe()
{
TextureHandle texture0;
TextureHandle texture1;
TextureHandle texture2;
TextureHandle texture3;
// First pass creates and writes two textures.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
texture1 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Second pass creates a transient texture => Create/Release should happen in this pass but we want to delay the release until the first graphics pipe pass that sync with async queue.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData))
{
texture2 = builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm });
builder.WriteTexture(texture0);
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// This pass is the last to read texture0 => Release should happen in this pass but we want to delay the release until the first graphics pipe pass that sync with async queue.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass2", out var passData))
{
texture0 = builder.ReadTexture(texture0);
builder.WriteTexture(texture1);
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Just here to add "padding" to the number of passes to ensure resources are not released right at the first sync pass.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass3", out var passData))
{
texture3 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.EnableAsyncCompute(false);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Pass prior to synchronization should be where textures are released.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass4", out var passData))
{
builder.WriteTexture(texture3);
builder.EnableAsyncCompute(false);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Graphics pass that reads texture1. This will request a sync with compute pipe. The previous pass should be the one releasing async textures.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass5", out var passData))
{
builder.ReadTexture(texture1);
builder.ReadTexture(texture3);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.EnableAsyncCompute(false);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(6, compiledPasses.size);
Assert.Contains(texture0.handle.index, compiledPasses[4].resourceReleaseList[(int)RenderGraphResourceType.Texture]);
Assert.Contains(texture2.handle.index, compiledPasses[4].resourceReleaseList[(int)RenderGraphResourceType.Texture]);
}
[Test]
public void TransientResourceNotCulled()
{
TextureHandle texture0;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
builder.CreateTransientTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm });
builder.WriteTexture(texture0);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Graphics pass that reads texture1. This will request a sync with compute pipe. The previous pass should be the one releasing async textures.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass5", out var passData))
{
builder.ReadTexture(texture0);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.EnableAsyncCompute(false);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(3, compiledPasses.size);
Assert.AreEqual(false, compiledPasses[1].culled);
}
[Test]
public void AsyncPassWriteWaitOnGraphcisPipe()
{
TextureHandle texture0;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData))
{
texture0 = builder.WriteTexture(texture0);
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData))
{
builder.ReadTexture(texture0);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(3, compiledPasses.size);
Assert.AreEqual(0, compiledPasses[1].syncToPassIndex);
Assert.AreEqual(1, compiledPasses[2].syncToPassIndex);
}
[Test]
public void AsyncPassReadWaitOnGraphcisPipe()
{
TextureHandle texture0;
TextureHandle texture1;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass1", out var passData))
{
builder.ReadTexture(texture0);
texture1 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData))
{
builder.ReadTexture(texture1);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(3, compiledPasses.size);
Assert.AreEqual(0, compiledPasses[1].syncToPassIndex);
Assert.AreEqual(1, compiledPasses[2].syncToPassIndex);
}
[Test]
public void GraphicsPassWriteWaitOnAsyncPipe()
{
TextureHandle texture0;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// This pass should sync with the "Async_TestPass0"
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
texture0 = builder.WriteTexture(texture0);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
// Read result and output to backbuffer to avoid culling passes.
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass2", out var passData))
{
builder.ReadTexture(texture0);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(3, compiledPasses.size);
Assert.AreEqual(0, compiledPasses[1].syncToPassIndex);
}
[Test]
public void GraphicsPassReadWaitOnAsyncPipe()
{
TextureHandle texture0;
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("Async_TestPass0", out var passData))
{
texture0 = builder.WriteTexture(m_RenderGraph.CreateTexture(new TextureDesc(Vector2.one) { colorFormat = GraphicsFormat.R8G8B8A8_UNorm }));
builder.EnableAsyncCompute(true);
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
using (var builder = m_RenderGraph.AddRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
{
builder.ReadTexture(texture0);
builder.WriteTexture(m_RenderGraph.ImportBackbuffer(0)); // Needed for the passes to not be culled
builder.SetRenderFunc((RenderGraphTestPassData data, RenderGraphContext context) => { });
}
m_RenderGraph.CompileRenderGraph();
var compiledPasses = m_RenderGraph.GetCompiledPassInfos();
Assert.AreEqual(2, compiledPasses.size);
Assert.AreEqual(0, compiledPasses[1].syncToPassIndex);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6a9714d04bc387489829e17fc14cf3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a869f30d596b98e41b8553703937b6e5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
Shader "DummyPipeline/VariantStrippingTestsShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderPipeline" = "DummyPipeline" "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 61e53729ad706d049b12a441f328fe34
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f5a8b36b9689f8a428381e6fd79a2278
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
{
"totalVariantsIn": 0,
"totalVariantsOut": 0,
"shaders": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d18eadffb612a2f4a995f89ad69c62c3
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
{
"totalVariantsIn": 200,
"totalVariantsOut": 100,
"shaders": [
{
"inputVariants": 100,
"outputVariants": 50,
"name": "UI/Default",
"pipelines": [
{
"inputVariants": 100,
"outputVariants": 50,
"pipeline": "",
"variants": [
{
"inputVariants": 0,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 0.0
},
{
"inputVariants": 10,
"outputVariants": 5,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 1.0
},
{
"inputVariants": 20,
"outputVariants": 10,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 2.0
},
{
"inputVariants": 30,
"outputVariants": 15,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 3.0
},
{
"inputVariants": 40,
"outputVariants": 20,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 4.0
}
]
}
]
},
{
"inputVariants": 100,
"outputVariants": 50,
"name": "Sprites/Default",
"pipelines": [
{
"inputVariants": 100,
"outputVariants": 50,
"pipeline": "",
"variants": [
{
"inputVariants": 0,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 0.0
},
{
"inputVariants": 10,
"outputVariants": 5,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 1.0
},
{
"inputVariants": 20,
"outputVariants": 10,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 2.0
},
{
"inputVariants": 30,
"outputVariants": 15,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 3.0
},
{
"inputVariants": 40,
"outputVariants": 20,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 4.0
}
]
}
]
}
]
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b7530f054ac4b4b4183bef9efddcb60f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
{
"totalVariantsIn": 200,
"totalVariantsOut": 0,
"shaders": [
{
"inputVariants": 100,
"outputVariants": 0,
"name": "UI/Default",
"pipelines": [
{
"inputVariants": 100,
"outputVariants": 0,
"pipeline": "",
"variants": [
{
"inputVariants": 0,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 0.0
},
{
"inputVariants": 10,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 1.0
},
{
"inputVariants": 20,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 2.0
},
{
"inputVariants": 30,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 3.0
},
{
"inputVariants": 40,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 4.0
}
]
}
]
},
{
"inputVariants": 100,
"outputVariants": 0,
"name": "Sprites/Default",
"pipelines": [
{
"inputVariants": 100,
"outputVariants": 0,
"pipeline": "",
"variants": [
{
"inputVariants": 0,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 0.0
},
{
"inputVariants": 10,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 1.0
},
{
"inputVariants": 20,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 2.0
},
{
"inputVariants": 30,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 3.0
},
{
"inputVariants": 40,
"outputVariants": 0,
"variantName": "Pass 0 (Normal) (SubShader: 0) (ShaderType: 0)",
"stripTimeMs": 4.0
}
]
}
]
}
]
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: edc0bb404a0b82742bb56f3c56b35ff4
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using NUnit.Framework;
using UnityEngine;
namespace UnityEditor.Rendering.Tests.ShaderStripping
{
class ShaderExtensionsTests
{
static TestCaseData[] s_TestCaseDatas =
{
new TestCaseData(Shader.Find("Hidden/Internal-Colored"))
.SetName("Given a shader from Built-in, the render pipeline tag is not found and is empty")
.Returns((false,string.Empty)),
new TestCaseData(Shader.Find("DummyPipeline/VariantStrippingTestsShader"))
.SetName("Given a shader with a render pipeline tag, the pipeline is found")
.Returns((true, "DummyPipeline"))
};
[Test, TestCaseSource(nameof(s_TestCaseDatas))]
public (bool, string) TryGetRenderPipelineTag(Shader shader)
{
return (shader.TryGetRenderPipelineTag(default, out string renderPipeline), renderPipeline);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d398fd9774b019e42ace1eb23b805f59
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
namespace UnityEditor.Rendering.Tests.ShaderStripping
{
class BuildReportTestScope : IDisposable
{
private ShaderStrippingReportScope m_Scope;
public BuildReportTestScope()
{
ShaderStrippingReportScope.s_DefaultExport = true;
m_Scope = new ShaderStrippingReportScope();
m_Scope.OnPreprocessBuild(default);
}
void IDisposable.Dispose()
{
m_Scope.OnPostprocessBuild(default);
m_Scope = null;
ShaderStrippingReportScope.s_DefaultExport = false;
}
}
public class ShaderStrippingReportTest
{
[UnitySetUp]
public void GlobalSetUp()
{
if (k_UpdateShaderStrippingResult && !Directory.Exists(k_ShaderResultsDirectory))
Directory.CreateDirectory(k_ShaderResultsDirectory);
}
static string k_ShaderResultsDirectory = "Packages/com.unity.render-pipelines.core/Tests/Editor/ShaderStripping/Results";
static bool k_UpdateShaderStrippingResult = false;
private void CheckForceUpdate(string path, string loggedStrippedResult)
{
if (k_UpdateShaderStrippingResult)
{
if (File.Exists(path))
File.Delete(path);
File.WriteAllText(path, loggedStrippedResult);
}
Assert.IsFalse(k_UpdateShaderStrippingResult, "Note to Developer: You forgot to set back the variable `k_UpdateShaderStrippingResult` to false");
}
static TestCaseData[] s_StrippedShaderInputs =
{
new TestCaseData( new Shader[] { Shader.Find("UI/Default") }, 0u, 0f)
.SetName("Given a shader with no variants, the result is not accumulated"),
new TestCaseData( new Shader[] { Shader.Find("UI/Default"), Shader.Find("Sprites/Default") }, 5u, 0f)
.SetName("Given a shader with all the variants stripped, the result is 0"),
new TestCaseData( new Shader[] { Shader.Find("UI/Default"), Shader.Find("Sprites/Default") }, 5u, .5f)
.SetName("Given a set of shaders, the result has the correct totals")
};
private void PerformFakeReport(Shader[] shaders, uint steps, float variantsOutMultiplier)
{
using (new BuildReportTestScope())
{
foreach (var shader in shaders)
{
for (uint i = 0; i < steps; ++i)
{
uint variantsIn = steps * 2 * i;
UnityEditor.Rendering.ShaderStripping.reporter.OnShaderProcessed<Shader, ShaderSnippetData>(shader, default, string.Empty, variantsIn, (uint)(variantsIn * variantsOutMultiplier), i);
}
}
}
}
[Test, TestCaseSource(nameof(s_StrippedShaderInputs))]
public void JSONOutput(Shader[] shaders, uint steps, float variantsOutMultiplier)
{
string fileName = $"{string.Join("_", shaders.Select(s => s.name))}_{steps}_{variantsOutMultiplier}.json".Replace("/", "_");
string path = Path.GetFullPath(Path.Combine(k_ShaderResultsDirectory, fileName));
PerformFakeReport(shaders, steps, variantsOutMultiplier);
var loggedStrippedResult = File.ReadAllText(ShaderStrippingReport.k_ShaderOutputPath);
CheckForceUpdate(path, loggedStrippedResult);
Assert.AreEqual(File.ReadAllText(path), loggedStrippedResult, "Note to Developer: You can set the variable `k_UpdateShaderStrippingResult` to true, to override the output if you changed what is reported");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d511d098696fe4249a56f343c93ae756
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Tests.ShaderStripping
{
public class VariantStrippingTests
{
#region Strippers
abstract class ShaderVariantStripperTest : IShaderVariantStripper
{
public bool active => false;
public abstract bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData shaderVariant, ShaderCompilerData shaderCompilerData);
}
class StripHalf : ShaderVariantStripperTest
{
private int m_InputDataCall;
public override bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData shaderVariant, ShaderCompilerData shaderCompilerData)
{
m_InputDataCall++;
return m_InputDataCall % 2 == 0;
}
}
class StripNothing : ShaderVariantStripperTest
{
public override bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData shaderVariant, ShaderCompilerData shaderCompilerData) => false;
}
class StripAll : ShaderVariantStripperTest
{
public override bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData shaderVariant, ShaderCompilerData shaderCompilerData) => true;
}
class CallbacksAreCalledStripper : ShaderVariantStripperTest, IShaderVariantStripperScope, IShaderVariantStripperSkipper
{
internal static List<string> s_Callbacks = new List<string> ();
public void AfterShaderStripping(Shader shader)
{
s_Callbacks.Add(nameof(AfterShaderStripping));
}
public void BeforeShaderStripping(Shader shader)
{
s_Callbacks.Add(nameof(BeforeShaderStripping));
}
public override bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData shaderVariant, ShaderCompilerData shaderCompilerData)
{
s_Callbacks.Add(nameof(CanRemoveVariant));
return false;
}
public bool SkipShader([DisallowNull] Shader shader, ShaderSnippetData shaderVariant)
{
s_Callbacks.Add(nameof(SkipShader));
return false;
}
}
class SkipReturnsTrue : ShaderVariantStripperTest, IShaderVariantStripperSkipper
{
internal static bool s_CanRemoveCalled = false;
internal static bool s_SkipShaderIsCalled = false;
public override bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData shaderVariant, ShaderCompilerData shaderCompilerData)
{
s_CanRemoveCalled = true;
return false;
}
public bool SkipShader([DisallowNull] Shader shader, ShaderSnippetData shaderVariant)
{
s_SkipShaderIsCalled = true;
return true;
}
}
class ShaderPrepocessorTests : ShaderPreprocessor<Shader, ShaderSnippetData>
{
public ShaderPrepocessorTests(Type type)
: base(new IVariantStripper<Shader, ShaderSnippetData>[] { Activator.CreateInstance(type) as IVariantStripper<Shader, ShaderSnippetData> })
{
}
public bool TryProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> inputData, out Exception error)
{
return TryStripShaderVariants(shader, snippet, inputData, out error);
}
}
#endregion
ShaderPrepocessorTests m_ShaderVariantStripper;
private RenderPipelineAsset m_PreviousPipeline;
ShaderStrippingReportScope m_ReportTestScope;
[SetUp]
public void Setup()
{
m_PreviousPipeline = GraphicsSettings.defaultRenderPipeline;
GraphicsSettings.defaultRenderPipeline = null;
m_ReportTestScope = new ShaderStrippingReportScope();
m_ReportTestScope.OnPreprocessBuild(default);
}
[TearDown]
public void TearDown()
{
m_ShaderVariantStripper = null;
GraphicsSettings.defaultRenderPipeline = m_PreviousPipeline;
m_ReportTestScope.OnPostprocessBuild(default);
m_ReportTestScope = null;
}
static TestCaseData[] s_TestCaseDatas =
{
new TestCaseData(typeof(StripNothing), Shader.Find("Hidden/Internal-Colored"), new List<Rendering.ShaderCompilerData> { default, default })
.SetName("Given a stripper that does nothing, the variants are kept")
.Returns(2),
new TestCaseData(typeof(StripAll), Shader.Find("Hidden/Internal-Colored"), new List<Rendering.ShaderCompilerData> { default, default })
.SetName("Given a stripper that strip everything, the variants are stripped")
.Returns(0),
new TestCaseData(typeof(StripHalf), Shader.Find("Hidden/Internal-Colored"), new List<Rendering.ShaderCompilerData> { default, default, default, default, default, default })
.SetName("Given a stripper that reduces the variants to the half, just half of the variants are stripped")
.Returns(3),
new TestCaseData(typeof(StripNothing), Shader.Find("DummyPipeline/VariantStrippingTestsShader"), new List<Rendering.ShaderCompilerData> { default, default })
.SetName("Given a shader that is not from the current pipeline, all the variants are stripped")
.Returns(0),
};
[Test, TestCaseSource(nameof(s_TestCaseDatas))]
public int CheckNumberOfVariantsIsCorrect(Type preprocessType, Shader shader, List<Rendering.ShaderCompilerData> variants)
{
m_ShaderVariantStripper = new (preprocessType);
Assert.IsTrue(m_ShaderVariantStripper.TryProcessShader(shader, default, variants, out var error));
return variants.Count;
}
static TestCaseData[] s_ExceptionTestCaseDatas =
{
new TestCaseData(typeof(StripAll), null, new List<Rendering.ShaderCompilerData> { default, default })
.SetName("Given a null shader, argument null exception is raised")
.Returns(typeof(ArgumentNullException)),
new TestCaseData(typeof(StripAll), Shader.Find("Hidden/Internal-Colored"), null)
.SetName("Given a null variants collection, argument null exception is raised")
.Returns(typeof(ArgumentNullException)),
};
[Test, TestCaseSource(nameof(s_ExceptionTestCaseDatas))]
public Type CheckExceptionsAreRaised(Type preprocessType, Shader shader, List<Rendering.ShaderCompilerData> variants)
{
m_ShaderVariantStripper = new(preprocessType);
Assert.IsFalse(m_ShaderVariantStripper.TryProcessShader(shader, default, variants, out var error));
return error.GetType();
}
static List<string> s_ExpectedCallbackOrder = new List<string>() {
nameof(CallbacksAreCalledStripper.BeforeShaderStripping),
nameof(CallbacksAreCalledStripper.SkipShader),
nameof(CallbacksAreCalledStripper.CanRemoveVariant),
nameof(CallbacksAreCalledStripper.AfterShaderStripping) };
[Test]
[Category("Callbacks")]
public void GivenAnStripperImplementingAllTheCallbacksTheyAreExecutedProperly()
{
CallbacksAreCalledStripper.s_Callbacks.Clear();
m_ShaderVariantStripper = new(typeof(CallbacksAreCalledStripper));
m_ShaderVariantStripper.TryProcessShader(Shader.Find("Hidden/Internal-Colored"), default, new List<Rendering.ShaderCompilerData> { default }, out var error);
Assert.AreEqual(s_ExpectedCallbackOrder, CallbacksAreCalledStripper.s_Callbacks);
}
[Test]
[Category("Callbacks")]
public void GivenAnStripperSkippingAShaderTheCallbackCanRemoveIsNotCalled()
{
m_ShaderVariantStripper = new(typeof(SkipReturnsTrue));
m_ShaderVariantStripper.TryProcessShader(Shader.Find("Hidden/Internal-Colored"), default, new List<Rendering.ShaderCompilerData> { default, default }, out var error);
Assert.IsTrue(SkipReturnsTrue.s_SkipShaderIsCalled, "IShaderVariantStripperSkipper.SkipShader was supossed to be called");
Assert.IsFalse(SkipReturnsTrue.s_CanRemoveCalled, "IVariantStripper.CanRemoveVariant was supossed to NOT be called");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 313a5f1eb564f37449f028ba1a9dbbea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using NUnit.Framework;
using System.Linq;
namespace UnityEditor.Rendering.Tests
{
class StringExtensionsTests
{
static TestCaseData[] s_Input =
{
new TestCaseData("A/B")
.Returns("A_B")
.SetName("Fogbugz - 1408027"),
new TestCaseData("A" + new string(System.IO.Path.GetInvalidFileNameChars()) + "B")
.Returns("A_B")
.SetName("All Chars Replaced"),
new TestCaseData("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ")
.Returns("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ")
.SetName("Nothing replaced")
};
[Test, TestCaseSource(nameof(s_Input))]
[Property("Fogbugz", "1408027")]
public string ReplaceInvalidFileNameCharacters(string input)
{
return input.ReplaceInvalidFileNameCharacters();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4d24867e495514419f555e6cd2f790d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Tests
{
public class SwapExtensionsTests
{
static TestCaseData[] s_ListTestsCaseDatas =
{
new TestCaseData(new int[] {1,2,3,4,5,6}, 0, 5)
.SetName("Swap first for last")
.Returns(new int[] {6,2,3,4,5,1}),
new TestCaseData(new int[] {1,2,3,4,5,6}, 5, 0)
.SetName("Swap last for first")
.Returns(new int[] {6,2,3,4,5,1}),
new TestCaseData(new int[] {1,2,3,4,5,6}, 1, 2)
.SetName("Swap elements in the middle")
.Returns(new int[] {1,3,2,4,5,6}),
new TestCaseData(new int[] {1,2,3,4,5,6}, 0, 2)
.SetName("Swap first for something in the middle")
.Returns(new int[] {3,2,1,4,5,6}),
new TestCaseData(new int[] {1,2,3,4,5,6}, 5, 2)
.SetName("Swap last for something in the middle")
.Returns(new int[] {1,2,6,4,5,3}),
};
[Test, TestCaseSource(nameof(s_ListTestsCaseDatas))]
public int[] TrySwap(int[] ints, int from, int to)
{
Assert.IsTrue(ints.TrySwap(from, to, out var _));
return ints;
}
static TestCaseData[] s_ListTestsCaseDatasExceptions =
{
new TestCaseData(null, -1, 1).SetName("Null list").Returns(typeof(ArgumentNullException)),
new TestCaseData(new int[] {1,2,3,4,5,6}, -1, 1).SetName("From negative").Returns(typeof(ArgumentOutOfRangeException)),
new TestCaseData(new int[] {1,2,3,4,5,6}, 6, 1).SetName("From larger than collection").Returns(typeof(ArgumentOutOfRangeException)),
new TestCaseData(new int[] {1,2,3,4,5,6}, 1, -1).SetName("To negative").Returns(typeof(ArgumentOutOfRangeException)),
new TestCaseData(new int[] {1,2,3,4,5,6}, 1, 6).SetName("To larger than collection").Returns(typeof(ArgumentOutOfRangeException)),
};
[Test, TestCaseSource(nameof(s_ListTestsCaseDatasExceptions))]
public Type ExceptionsAreCorrect(int[] ints, int from, int to)
{
ints.TrySwap(from, to, out var error);
return error.GetType();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 170e625407be98640be7a881672523ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
{
"name": "Unity.RenderPipelines.Core.Editor.Tests",
"references": [
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:3eae0364be2026648bf74846acb8a731",
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6e5480588ffa37d4f82fe96c45e8ce9c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fba05ad04ca734045bd6708fb9c77773
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using System.Diagnostics;
using NUnit.Framework;
using System.Threading;
using UnityEditor.Rendering;
namespace UnityEngine.Rendering.Utils.Tests
{
public class TimedScopeTests
{
private const int k_MillisecondsTimeout = 1000;
private const int k_TimeComparisonDelta = 20;
[Test]
[Ignore("Test is not stable for CI.")]
public unsafe void SimpleTimeCheckFromPtr()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
double timedScopeTime = 0;
using (TimedScope.FromPtr(&timedScopeTime))
{
Thread.Sleep(k_MillisecondsTimeout);
}
stopwatch.Stop();
double stopWatchTime = stopwatch.Elapsed.TotalMilliseconds;
Assert.AreEqual(stopWatchTime, timedScopeTime, k_TimeComparisonDelta);
}
[Test]
[Ignore("Test is not stable for CI.")]
public void SimpleTimeCheckFromRef()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
double timedScopeTime = 0;
using (TimedScope.FromRef(ref timedScopeTime))
{
Thread.Sleep(k_MillisecondsTimeout);
}
stopwatch.Stop();
double stopWatchTime = stopwatch.Elapsed.TotalMilliseconds;
Assert.AreEqual(stopWatchTime, timedScopeTime, k_TimeComparisonDelta);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d59aca2eddb92494fb12dcc2792e8ac7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 93ab3f290033d9a48b3cce3d26d93f89
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,369 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Rendering;
using ActionTest = System.Action<UnityEngine.AnimationCurve, UnityEngine.AnimationCurve, UnityEngine.AnimationCurve, UnityEngine.Rendering.VolumeStack>;
namespace UnityEngine.Rendering.Tests
{
public class VolumeComponentAnimCurveTests
{
#region Interpolation
static bool TestAnimationCurveInterp(AnimationCurve lhsCurve, AnimationCurve rhsCurve, float t, float startTime, float endTime, int numSteps, float eps, bool debugPrint)
{
AnimationCurve midCurve = new AnimationCurve(lhsCurve.keys);
KeyframeUtility.InterpAnimationCurve(ref midCurve, rhsCurve, t);
for (int i = 0; i <= numSteps; i++)
{
float timeT = ((float)i) / ((float)numSteps);
float currTime = Mathf.Lerp(startTime, endTime, timeT);
float lhsVal = lhsCurve.Evaluate(currTime);
float rhsVal = rhsCurve.Evaluate(currTime);
float expectedVal = Mathf.Lerp(lhsVal, rhsVal, t);
float actualVal = midCurve.Evaluate(currTime);
float offset = actualVal - expectedVal;
if (debugPrint)
{
Debug.Log(i.ToString() + ": " + offset.ToString());
}
if (Mathf.Abs(offset) >= eps)
{
return false;
}
}
return true;
}
static AnimationCurve CreateTestCurve(int index)
{
AnimationCurve testCurve = new AnimationCurve();
if (index == 0)
{
testCurve.AddKey(new Keyframe(0.0f, 3.0f, 2.0f, 2.0f));
testCurve.AddKey(new Keyframe(4.0f, 2.0f, -1.0f, -1.0f));
testCurve.AddKey(new Keyframe(7.0f, 2.6f, -1.0f, -1.0f));
}
else if (index == 1)
{
testCurve.AddKey(new Keyframe(-1.0f, 3.0f, 2.0f, 2.0f));
testCurve.AddKey(new Keyframe(4.0f, 2.0f, 3.0f, 3.0f));
testCurve.AddKey(new Keyframe(5.0f, 2.6f, 0.0f, 0.0f));
testCurve.AddKey(new Keyframe(9.0f, 2.6f, -5.0f, -5.0f));
}
else if (index == 2)
{
// Needed for the same positions as curve 0 but different values and tangents
testCurve.AddKey(new Keyframe(0.0f, 1.0f, -1.0f, 3.0f));
testCurve.AddKey(new Keyframe(4.0f, 6.0f, -9.0f, -2.0f));
testCurve.AddKey(new Keyframe(7.0f, 5.2f, -3.0f, -4.0f));
}
else
{
// Need for the test case where two curves have no overlap
testCurve.AddKey(new Keyframe(11.0f, 1.0f, -1.0f, 3.0f));
testCurve.AddKey(new Keyframe(14.0f, 6.0f, -9.0f, -2.0f));
testCurve.AddKey(new Keyframe(17.0f, 5.2f, -3.0f, -4.0f));
}
return testCurve;
}
static TestCaseData[] s_AnimationCurveTestDatas =
{
new TestCaseData(CreateTestCurve(0), CreateTestCurve(1), 0.25f)
.SetName("CurveTest 1"),
new TestCaseData(CreateTestCurve(1), CreateTestCurve(2), 0.25f)
.SetName("CurveTest 2"),
new TestCaseData(CreateTestCurve(0), CreateTestCurve(2), 0.25f)
.SetName("CurveTest Same Positions"),
new TestCaseData(CreateTestCurve(0), CreateTestCurve(3), 0.25f)
.SetName("CurveTest No Overlap"),
};
[Test, TestCaseSource(nameof(s_AnimationCurveTestDatas))]
public void RenderInterpolateAnimationCurve(AnimationCurve lhsCurve, AnimationCurve rhsCurve, float t)
{
Assert.IsTrue(TestAnimationCurveInterp(lhsCurve, rhsCurve, t, -5.0f, 20.0f, 100, 1e-5f, false));
}
#endregion
class TestAnimationCurveVolumeComponent : VolumeComponent
{
public AnimationCurveParameter testParameter = new (AnimationCurve.Linear(0.5f, 10.0f, 1.0f, 15.0f), true);
}
static TestCaseData[] s_AnimationCurveKeysNotSharedTestDatas =
{
new TestCaseData(null)
.SetName("Reloading the stack makes the parameters be the same as TestAnimationCurveVolumeComponent")
.Returns((2,2,2)),
new TestCaseData((ActionTest)((parameterInterpolated, _, _, stack) =>
{
// The replace data will call: AnimationCurveParameter.SetValue make sure the C++ reference is not shared
VolumeManager.instance.ReplaceData(stack);
// Check that the value that stores the interpolated data, if is modified both default values are modified
parameterInterpolated.RemoveKey(1);
}))
.SetName("When Replacing the current interpolated values by the ones in the default, applying modifications to the interpolated parameter do not modify the default parameters")
.Returns((1,2,2)),
new TestCaseData((ActionTest)((_, _, defaultComponentParameterUsedToInitializeStack, _) =>
{
defaultComponentParameterUsedToInitializeStack.AddKey(0.0f, 1.0f);
}))
.SetName("When modifying the default component used to initialize the stack, the parameters on the stack remain the same, as they should be cloned")
.Returns((2,2,3)),
new TestCaseData((ActionTest)((_, defaultParameterForFastAccess, _, _) =>
{
defaultParameterForFastAccess.AddKey(0.0f, 1.0f);
defaultParameterForFastAccess.AddKey(0.6f, 2.0f);
}))
.SetName("Check that the default parameter on the stack do not modifies the interpolated value or either the default used to initialize the stack")
.Returns((2,4,2)),
new TestCaseData((ActionTest)((_, defaultParameterForFastAccess, _, stack) =>
{
defaultParameterForFastAccess.AddKey(0.0f, 1.0f);
defaultParameterForFastAccess.AddKey(0.6f, 2.0f);
VolumeManager.instance.ReplaceData(stack);
}))
.SetName("Check that ReplaceData should have modified the interpolated value with the default value stored in the stack and not the one used from the default")
.Returns((4,4,2)),
new TestCaseData((ActionTest)((_, _, _, stack) =>
{
stack.Clear();
}))
.SetName("Check that clearing the stack should modify and release memory from the parameters and volume components that have locally in the stack, but not the default volume used to initialize the stack")
.Returns((-1,-1,2)),
};
private TestAnimationCurveVolumeComponent m_DefaultComponent;
[SetUp]
public void Setup()
{
m_DefaultComponent = ScriptableObject.CreateInstance<TestAnimationCurveVolumeComponent>();
}
[TearDown]
public void TearDown()
{
ScriptableObject.DestroyImmediate(m_DefaultComponent);
}
[Test, Description("UUM-20458, UUM-20456"), TestCaseSource(nameof(s_AnimationCurveKeysNotSharedTestDatas))]
public (int, int, int) AnimationCurveParameterKeysAreNotShared(ActionTest actionToPerform)
{
using var stack = new VolumeStack();
// Initialize the stack
stack.Reload(new List<VolumeComponent>() { m_DefaultComponent });
actionToPerform?.Invoke(
stack.defaultParameters[0].parameter.GetValue<AnimationCurve>(), // parameterInterpolated
stack.defaultParameters[0].defaultValue.GetValue<AnimationCurve>(), // defaultParameterForFastAccess
m_DefaultComponent.testParameter.GetValue<AnimationCurve>(), // defaultComponentParameterUsedToInitializeStack
stack);
return (
stack.defaultParameters == null ? -1 : stack.defaultParameters[0].parameter.GetValue<AnimationCurve>().length, // parameterInterpolated
stack.defaultParameters == null ? -1 : stack.defaultParameters[0].defaultValue.GetValue<AnimationCurve>().length, // defaultParameterForFastAccess
m_DefaultComponent.testParameter.GetValue<AnimationCurve>().length // defaultComponentParameterUsedToInitializeStack
);
}
}
public class VolumeComponentEditorTests
{
[HideInInspector]
[VolumeComponentMenuForRenderPipeline("Tests/No Additional", typeof(RenderPipeline))]
class VolumeComponentNoAdditionalAttributes : VolumeComponent
{
public MinFloatParameter parameter = new MinFloatParameter(0f, 0f);
}
[HideInInspector]
[VolumeComponentMenuForRenderPipeline("Tests/All Additional", typeof(RenderPipeline))]
class VolumeComponentAllAdditionalAttributes : VolumeComponent
{
[AdditionalProperty]
public MinFloatParameter parameter1 = new MinFloatParameter(0f, 0f);
[AdditionalProperty]
public FloatParameter parameter2 = new MinFloatParameter(0f, 0f);
}
[HideInInspector]
[VolumeComponentMenuForRenderPipeline("Tests/Mixed Additional", typeof(RenderPipeline))]
class VolumeComponentMixedAdditionalAttributes : VolumeComponent
{
public MinFloatParameter parameter1 = new MinFloatParameter(0f, 0f);
[AdditionalProperty]
public FloatParameter parameter2 = new MinFloatParameter(0f, 0f);
public MinFloatParameter parameter3 = new MinFloatParameter(0f, 0f);
[AdditionalProperty]
public FloatParameter parameter4 = new MinFloatParameter(0f, 0f);
}
private void CreateEditorAndComponent(Type volumeComponentType, ref VolumeComponent component, ref VolumeComponentEditor editor)
{
component = (VolumeComponent)ScriptableObject.CreateInstance(volumeComponentType);
editor = (VolumeComponentEditor)Editor.CreateEditor(component);
editor.Invoke("Init");
}
[Test]
public void TestOverridesChanges()
{
VolumeComponent component = null;
VolumeComponentEditor editor = null;
CreateEditorAndComponent(typeof(VolumeComponentMixedAdditionalAttributes), ref component, ref editor);
component.SetAllOverridesTo(false);
bool allOverridesState = (bool)editor.Invoke("AreAllOverridesTo", false);
Assert.True(allOverridesState);
component.SetAllOverridesTo(true);
// Was the change correct?
allOverridesState = (bool)editor.Invoke("AreAllOverridesTo", true);
Assert.True(allOverridesState);
// Enable the advance mode on the editor
editor.showAdditionalProperties = true;
// Everything is false
component.SetAllOverridesTo(false);
// Disable the advance mode on the editor
editor.showAdditionalProperties = false;
// Now just set to true the overrides of non additional properties
editor.Invoke("SetOverridesTo", true);
// Check that the non additional properties must be false
allOverridesState = (bool)editor.Invoke("AreAllOverridesTo", true);
Assert.False(allOverridesState);
ScriptableObject.DestroyImmediate(component);
}
static TestCaseData[] s_AdditionalAttributesTestCaseDatas =
{
new TestCaseData(typeof(VolumeComponentNoAdditionalAttributes))
.Returns(Array.Empty<string>())
.SetName("VolumeComponentNoAdditionalAttributes"),
new TestCaseData(typeof(VolumeComponentAllAdditionalAttributes))
.Returns(new string[2] {"parameter1", "parameter2"})
.SetName("VolumeComponentAllAdditionalAttributes"),
new TestCaseData(typeof(VolumeComponentMixedAdditionalAttributes))
.Returns(new string[2] {"parameter2", "parameter4"})
.SetName("VolumeComponentMixedAdditionalAttributes"),
};
[Test, TestCaseSource(nameof(s_AdditionalAttributesTestCaseDatas))]
public string[] AdditionalProperties(Type volumeComponentType)
{
VolumeComponent component = null;
VolumeComponentEditor editor = null;
CreateEditorAndComponent(volumeComponentType, ref component, ref editor);
var fields = component
.GetFields()
.Where(f => f.GetCustomAttribute<AdditionalPropertyAttribute>() != null)
.Select(f => f.Name)
.ToArray();
var notAdditionalParameters = editor.GetField("m_VolumeNotAdditionalParameters") as List<VolumeParameter>;
Assert.True(fields.Count() + notAdditionalParameters.Count == component.parameters.Count);
ScriptableObject.DestroyImmediate(component);
return fields;
}
#region Decorators Handling Test
[HideInInspector]
class VolumeComponentDecorators : VolumeComponent
{
[Tooltip("Increase to make the noise texture appear bigger and less")]
public FloatParameter _NoiseTileSize = new FloatParameter(25.0f);
[InspectorName("Color")]
public ColorParameter _FogColor = new ColorParameter(Color.grey);
[InspectorName("Size and occurrence"), Tooltip("Increase to make patches SMALLER, and frequent")]
public ClampedFloatParameter _HighNoiseSpaceFreq = new ClampedFloatParameter(0.1f, 0.1f, 1f);
}
readonly (string displayName, string tooltip)[] k_ExpectedResults =
{
(string.Empty, "Increase to make the noise texture appear bigger and less"),
("Color", string.Empty),
("Size and occurrence", "Increase to make patches SMALLER, and frequent")
};
[Test]
public void TestHandleParameterDecorators()
{
VolumeComponent component = null;
VolumeComponentEditor editor = null;
CreateEditorAndComponent(typeof(VolumeComponentDecorators), ref component, ref editor);
var parameters =
editor.GetField("m_Parameters") as List<(GUIContent displayName, int displayOrder,
SerializedDataParameter param)>;
Assert.True(parameters != null && parameters.Count() == k_ExpectedResults.Count());
for (int i = 0; i < k_ExpectedResults.Count(); ++i)
{
var property = parameters[i].param;
var title = new GUIContent(parameters[i].displayName);
editor.Invoke("HandleDecorators", property, title);
Assert.True(k_ExpectedResults[i].displayName == title.text);
Assert.True(k_ExpectedResults[i].tooltip == title.tooltip);
}
ScriptableObject.DestroyImmediate(component);
}
#endregion
[Test]
public void TestSupportedOnAvoidedIfHideInInspector()
{
Type[] types = new[]
{
typeof(VolumeComponentNoAdditionalAttributes),
typeof(VolumeComponentAllAdditionalAttributes),
typeof(VolumeComponentMixedAdditionalAttributes)
};
Type volumeComponentProvider = ReflectionUtils.FindTypeByName("UnityEngine.Rendering.VolumeManager");
var volumeComponents = volumeComponentProvider.InvokeStatic("FilterVolumeComponentTypes",
types, typeof(RenderPipeline)) as List<(string, Type)>;
Assert.NotNull(volumeComponents);
Assert.False(volumeComponents.Any());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fd9ee276a1023e439cf7a9c393195fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a189334a4e37b1247af4cfcedc20802c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using System;
using NUnit.Framework;
using UnityEngine.Rendering;
class DebugMangerTests
{
[Test]
public void WindowStateCallbackIsTriggerred()
{
bool called = false;
Action<DebugManager.UIMode, bool> action = (mode, open) =>
{
Assert.AreEqual(DebugManager.UIMode.EditorMode, mode);
Assert.AreEqual(true, open);
called = true;
};
DebugManager.windowStateChanged += action;
DebugManager.instance.displayEditorUI = true;
Assert.AreEqual(true, called);
DebugManager.windowStateChanged -= action;
DebugManager.instance.displayEditorUI = false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a191d0939f271d4b9b583b27f1ecd2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;
class RuntimeExampleTest
{
[Test]
public void PlayModeSampleTestSimplePasses()
{
// Use the Assert class to test conditions.
}
// A UnityTest behaves like a coroutine in PlayMode
// and allows you to yield null to skip a frame in EditMode
[UnityTest]
public IEnumerator PlayModeSampleTestWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// yield to skip a frame
yield return null;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a99028421222694897393dabe2744fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using NUnit.Framework;
namespace UnityEngine.Rendering.Tests
{
class RuntimeProfilerTestBase
{
protected const int k_NumWarmupFrames = 10;
protected const int k_NumFramesToRender = 30;
protected DebugFrameTiming m_DebugFrameTiming;
protected GameObject m_ToCleanup;
[SetUp]
public void Setup()
{
if (!FrameTimingManager.IsFeatureEnabled())
Assert.Ignore("Frame timing stats are disabled in Player Settings, skipping test.");
if (Application.isBatchMode)
Assert.Ignore("Frame timing tests are not supported in batch mode, skipping test.");
// HACK #1 - really shouldn't have to do this here, but previous tests are leaking gameobjects
var objects = GameObject.FindObjectsOfType<GameObject>();
foreach (var o in objects)
{
// HACK #2 - must not destroy DebugUpdater, which happens to have an EventSystem.
if (o.GetComponent<EventSystem>() == null)
CoreUtils.Destroy(o);
}
m_DebugFrameTiming = new DebugFrameTiming();
}
[TearDown]
public void TearDown()
{
if (m_ToCleanup != null)
CoreUtils.Destroy(m_ToCleanup);
}
protected IEnumerator Warmup()
{
for (int i = 0; i < k_NumWarmupFrames; i++)
yield return null;
m_DebugFrameTiming.Reset();
}
}
// [UnityPlatform(exclude = new RuntimePlatform[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.LinuxEditor })] // Disabled on Linux (case 1370861)
// class RuntimeProfilerTests : RuntimeProfilerTestBase
// {
// [UnityTest]
// public IEnumerator RuntimeProfilerGivesNonZeroOutput()
// {
// yield return Warmup();
// m_ToCleanup = new GameObject();
// var camera = m_ToCleanup.AddComponent<Camera>();
// for (int i = 0; i < k_NumFramesToRender; i++)
// {
// m_DebugFrameTiming.UpdateFrameTiming();
// camera.Render();
// yield return null;
// }
// Assert.True(
// m_DebugFrameTiming.m_BottleneckHistory.Histogram.Balanced > 0 ||
// m_DebugFrameTiming.m_BottleneckHistory.Histogram.CPU > 0 ||
// m_DebugFrameTiming.m_BottleneckHistory.Histogram.GPU > 0 ||
// m_DebugFrameTiming.m_BottleneckHistory.Histogram.PresentLimited > 0);
// }
// }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 40a73c4a33c05924f813df35844d8c71
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
{
"name": "Unity.RenderPipelines.Core.Runtime.Tests",
"references": [
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bf043f86dbf1bda4398ec83eebe40b8c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: