You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
407 lines
20 KiB
407 lines
20 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Collections;
|
|
using static UnityEngine.Rendering.DebugUI;
|
|
using static UnityEngine.Rendering.DebugUI.Widget;
|
|
|
|
namespace UnityEngine.Rendering
|
|
{
|
|
/// <summary>
|
|
/// GPU Resident Drawer Rendering Debugger settings.
|
|
/// </summary>
|
|
public class DebugDisplayGPUResidentDrawer : IDebugDisplaySettingsData
|
|
{
|
|
const string k_FormatString = "{0}";
|
|
const float k_RefreshRate = 1f / 5f;
|
|
const int k_MaxViewCount = 32;
|
|
const int k_MaxOcclusionPassCount = 32;
|
|
const int k_MaxContextCount = 16;
|
|
|
|
private bool displayBatcherStats
|
|
{
|
|
get
|
|
{
|
|
return GPUResidentDrawer.GetDebugStats()?.enabled ?? false;
|
|
}
|
|
set
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null)
|
|
debugStats.enabled = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>Returns the view instances id for the selected occluder debug view index, or 0 if not valid.</summary>
|
|
internal bool GetOccluderViewInstanceID(out int viewInstanceID)
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null)
|
|
{
|
|
if (occluderDebugViewIndex >= 0 && occluderDebugViewIndex < debugStats.occluderStats.Length)
|
|
{
|
|
viewInstanceID = debugStats.occluderStats[occluderDebugViewIndex].viewInstanceID;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
viewInstanceID = 0;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>Returns if the occlusion test heatmap debug overlay is enabled.</summary>
|
|
internal bool occlusionTestOverlayEnable
|
|
{
|
|
get { return GPUResidentDrawer.GetDebugStats()?.occlusionOverlayEnabled ?? false; }
|
|
set
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null)
|
|
debugStats.occlusionOverlayEnabled = value;
|
|
}
|
|
}
|
|
|
|
private bool occlusionTestOverlayCountVisible
|
|
{
|
|
get { return GPUResidentDrawer.GetDebugStats()?.occlusionOverlayCountVisible ?? false; }
|
|
set
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null)
|
|
debugStats.occlusionOverlayCountVisible = value;
|
|
}
|
|
}
|
|
|
|
private bool overrideOcclusionTestToAlwaysPass
|
|
{
|
|
get { return GPUResidentDrawer.GetDebugStats()?.overrideOcclusionTestToAlwaysPass ?? false; }
|
|
set
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null)
|
|
debugStats.overrideOcclusionTestToAlwaysPass = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>Returns true if the occluder debug overlay is enabled.</summary>
|
|
public bool occluderDebugViewEnable = false;
|
|
|
|
internal bool occluderContextStats = false;
|
|
internal Vector2 occluderDebugViewRange = new Vector2(0.0f, 1.0f);
|
|
internal int occluderDebugViewIndex = 0;
|
|
|
|
private static InstanceCullerViewStats GetInstanceCullerViewStats(int viewIndex)
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null && viewIndex < debugStats.instanceCullerStats.Length)
|
|
return debugStats.instanceCullerStats[viewIndex];
|
|
else
|
|
return new InstanceCullerViewStats();
|
|
}
|
|
|
|
private static InstanceOcclusionEventStats GetInstanceOcclusionEventStats(int passIndex)
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null && passIndex < debugStats.instanceOcclusionEventStats.Length)
|
|
return debugStats.instanceOcclusionEventStats[passIndex];
|
|
else
|
|
return new InstanceOcclusionEventStats();
|
|
}
|
|
static class Strings
|
|
{
|
|
public const string drawerSettingsContainerName = "GPU Resident Drawer Settings";
|
|
public static readonly NameAndTooltip displayBatcherStats = new() { name = "Display Culling Stats", tooltip = "Enable the checkbox to display stats for instance culling." };
|
|
public const string occlusionCullingTitle = "Occlusion Culling";
|
|
public static readonly NameAndTooltip occlusionTestOverlayEnable = new() { name = "Occlusion Test Overlay", tooltip = "Occlusion test visualisation." };
|
|
public static readonly NameAndTooltip occlusionTestOverlayCountVisible = new() { name = "Occlusion Test Overlay Count Visible", tooltip = "Occlusion test visualisation should count visible instances instead of occluded instances." };
|
|
public static readonly NameAndTooltip overrideOcclusionTestToAlwaysPass = new() { name = "Override Occlusion Test To Always Pass", tooltip = "Occlusion test always passes." };
|
|
public static readonly NameAndTooltip occluderContextStats = new() { name = "Occluder Context Stats", tooltip = "Show all the active occluder context textures." };
|
|
public static readonly NameAndTooltip occluderDebugViewEnable = new() { name = "Occluder Debug View", tooltip = "Debug view of occluder texture." };
|
|
public static readonly NameAndTooltip occluderDebugViewIndex = new() { name = "Occluder Debug View Index", tooltip = "Index of the view for which the occluder texture is displayed. Use the Occlusion Test Context Stats for a list of the views." };
|
|
public static readonly NameAndTooltip occluderDebugViewRangeMin = new() { name = "Occluder Debug View Range Min", tooltip = "Range in which the occluder debug texture are displayed." };
|
|
public static readonly NameAndTooltip occluderDebugViewRangeMax = new() { name = "Occluder Debug View Range Max", tooltip = "Range in which the occluder debug texture are displayed." };
|
|
}
|
|
|
|
private static DebugOccluderStats GetOccluderStats(int occluderIndex)
|
|
{
|
|
DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats();
|
|
if (debugStats != null && occluderIndex < debugStats.occluderStats.Length)
|
|
return debugStats.occluderStats[occluderIndex];
|
|
else
|
|
return new DebugOccluderStats();
|
|
}
|
|
|
|
private static int GetOcclusionContextsCounts()
|
|
{
|
|
return GPUResidentDrawer.GetDebugStats()?.occluderStats.Length ?? 0;
|
|
}
|
|
|
|
private static int GetInstanceCullerViewCount()
|
|
{
|
|
return GPUResidentDrawer.GetDebugStats()?.instanceCullerStats.Length ?? 0;
|
|
}
|
|
|
|
private static int GetInstanceOcclusionEventCount()
|
|
{
|
|
return GPUResidentDrawer.GetDebugStats()?.instanceOcclusionEventStats.Length ?? 0;
|
|
}
|
|
private static DebugUI.Table.Row AddInstanceCullerViewDataRow(int viewIndex)
|
|
{
|
|
return new DebugUI.Table.Row
|
|
{
|
|
displayName = "",
|
|
opened = true,
|
|
isHiddenCallback = () => { return viewIndex >= GetInstanceCullerViewCount(); },
|
|
children =
|
|
{
|
|
new DebugUI.Value { displayName = "View Type", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).viewType },
|
|
new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).viewInstanceID },
|
|
new DebugUI.Value { displayName = "Split Index", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).splitIndex },
|
|
new DebugUI.Value { displayName = "Visible Instances", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).visibleInstances },
|
|
new DebugUI.Value { displayName = "Draw Commands", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).drawCommands },
|
|
}
|
|
};
|
|
}
|
|
|
|
private static object OccluderVersionString(in InstanceOcclusionEventStats stats)
|
|
{
|
|
return (stats.eventType == InstanceOcclusionEventType.OccluderUpdate || stats.occlusionTest != OcclusionTest.None) ? stats.occluderVersion : "-";
|
|
}
|
|
|
|
private static object OcclusionTestString(in InstanceOcclusionEventStats stats)
|
|
{
|
|
return (stats.eventType == InstanceOcclusionEventType.OcclusionTest) ? stats.occlusionTest : "-";
|
|
}
|
|
|
|
private static object VisibleInstancesString(in InstanceOcclusionEventStats stats)
|
|
{
|
|
return (stats.eventType == InstanceOcclusionEventType.OcclusionTest) ? stats.visibleInstances : "-";
|
|
}
|
|
|
|
private static object CulledInstancesString(in InstanceOcclusionEventStats stats)
|
|
{
|
|
return (stats.eventType == InstanceOcclusionEventType.OcclusionTest) ? stats.culledInstances : "-";
|
|
}
|
|
|
|
private static DebugUI.Table.Row AddInstanceOcclusionPassDataRow(int eventIndex)
|
|
{
|
|
return new DebugUI.Table.Row
|
|
{
|
|
displayName = "",
|
|
opened = true,
|
|
isHiddenCallback = () => { return eventIndex >= GetInstanceOcclusionEventCount(); },
|
|
children =
|
|
{
|
|
new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceOcclusionEventStats(eventIndex).viewInstanceID },
|
|
new DebugUI.Value { displayName = "Event Type", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"{GetInstanceOcclusionEventStats(eventIndex).eventType}" },
|
|
new DebugUI.Value { displayName = "Occluder Version", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => OccluderVersionString(GetInstanceOcclusionEventStats(eventIndex)) },
|
|
new DebugUI.Value { displayName = "Subview Mask", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"0x{GetInstanceOcclusionEventStats(eventIndex).subviewMask:X}" },
|
|
new DebugUI.Value { displayName = "Occlusion Test", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"{OcclusionTestString(GetInstanceOcclusionEventStats(eventIndex))}" },
|
|
new DebugUI.Value { displayName = "Visible Instances", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => VisibleInstancesString(GetInstanceOcclusionEventStats(eventIndex)) },
|
|
new DebugUI.Value { displayName = "Culled Instances", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => CulledInstancesString(GetInstanceOcclusionEventStats(eventIndex)) },
|
|
}
|
|
};
|
|
}
|
|
|
|
private static DebugUI.Table.Row AddOcclusionContextDataRow(int index)
|
|
{
|
|
return new DebugUI.Table.Row
|
|
{
|
|
displayName = "",
|
|
opened = true,
|
|
isHiddenCallback = () => index >= GetOcclusionContextsCounts(),
|
|
children =
|
|
{
|
|
new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetOccluderStats(index).viewInstanceID },
|
|
new DebugUI.Value { displayName = "Subview Count", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetOccluderStats(index).subviewCount },
|
|
new DebugUI.Value { displayName = "Size Per Subview", refreshRate = k_RefreshRate, formatString = k_FormatString, getter =
|
|
() =>
|
|
{
|
|
Vector2Int size = GetOccluderStats(index).occluderMipLayoutSize;
|
|
return $"{size.x}x{size.y}";
|
|
}},
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
[DisplayInfo(name = "GPU Resident Drawer", order = 5)]
|
|
private class SettingsPanel : DebugDisplaySettingsPanel
|
|
{
|
|
public override string PanelName => "GPU Resident Drawer";
|
|
|
|
public override DebugUI.Flags Flags => DebugUI.Flags.EditorForceUpdate;
|
|
|
|
public SettingsPanel(DebugDisplayGPUResidentDrawer data)
|
|
{
|
|
var helpBox = new DebugUI.MessageBox()
|
|
{
|
|
displayName = "Not Supported",
|
|
style = MessageBox.Style.Warning,
|
|
messageCallback = () =>
|
|
{
|
|
var settings = GPUResidentDrawer.GetGlobalSettingsFromRPAsset();
|
|
return GPUResidentDrawer.IsGPUResidentDrawerSupportedBySRP(settings, out var msg, out var _) ? string.Empty : msg;
|
|
},
|
|
isHiddenCallback = () => GPUResidentDrawer.IsEnabled()
|
|
};
|
|
|
|
AddWidget(helpBox);
|
|
|
|
AddWidget(new Container()
|
|
{
|
|
displayName = Strings.occlusionCullingTitle,
|
|
isHiddenCallback = () => !GPUResidentDrawer.IsEnabled(),
|
|
children =
|
|
{
|
|
new DebugUI.BoolField { nameAndTooltip = Strings.occlusionTestOverlayEnable, getter = () => data.occlusionTestOverlayEnable, setter = value => data.occlusionTestOverlayEnable = value},
|
|
new DebugUI.BoolField { nameAndTooltip = Strings.occlusionTestOverlayCountVisible, getter = () => data.occlusionTestOverlayCountVisible, setter = value => data.occlusionTestOverlayCountVisible = value},
|
|
new DebugUI.BoolField { nameAndTooltip = Strings.overrideOcclusionTestToAlwaysPass, getter = () => data.overrideOcclusionTestToAlwaysPass, setter = value => data.overrideOcclusionTestToAlwaysPass = value},
|
|
new DebugUI.BoolField { nameAndTooltip = Strings.occluderContextStats, getter = () => data.occluderContextStats, setter = value => data.occluderContextStats = value},
|
|
new DebugUI.BoolField { nameAndTooltip = Strings.occluderDebugViewEnable, getter = () => data.occluderDebugViewEnable, setter = value => data.occluderDebugViewEnable = value},
|
|
new DebugUI.IntField { nameAndTooltip = Strings.occluderDebugViewIndex, getter = () => data.occluderDebugViewIndex, setter = value => data.occluderDebugViewIndex = value, isHiddenCallback = () => !data.occluderDebugViewEnable, min = () => 0, max = () => Math.Max(GetOcclusionContextsCounts() - 1, 0) },
|
|
new DebugUI.FloatField {nameAndTooltip = Strings.occluderDebugViewRangeMin, getter = () => data.occluderDebugViewRange.x, setter = value => data.occluderDebugViewRange.x = value, isHiddenCallback = () => !data.occluderDebugViewEnable},
|
|
new DebugUI.FloatField {nameAndTooltip = Strings.occluderDebugViewRangeMax, getter = () => data.occluderDebugViewRange.y, setter = value => data.occluderDebugViewRange.y = value, isHiddenCallback = () => !data.occluderDebugViewEnable}
|
|
}
|
|
});
|
|
AddOcclusionContextStatsWidget(data);
|
|
|
|
AddWidget(new DebugUI.Container()
|
|
{
|
|
displayName = Strings.drawerSettingsContainerName,
|
|
isHiddenCallback = () => !GPUResidentDrawer.IsEnabled(),
|
|
children =
|
|
{
|
|
new DebugUI.BoolField { nameAndTooltip = Strings.displayBatcherStats, getter = () => data.displayBatcherStats, setter = value => data.displayBatcherStats = value},
|
|
}
|
|
});
|
|
|
|
AddInstanceCullingStatsWidget(data);
|
|
}
|
|
|
|
private void AddInstanceCullingStatsWidget(DebugDisplayGPUResidentDrawer data)
|
|
{
|
|
var instanceCullerStats = new DebugUI.Foldout
|
|
{
|
|
displayName = "Instance Culler Stats",
|
|
isHeader = true,
|
|
opened = true,
|
|
isHiddenCallback = () => !data.displayBatcherStats
|
|
};
|
|
|
|
instanceCullerStats.children.Add(new DebugUI.ValueTuple()
|
|
{
|
|
displayName = "View Count",
|
|
values = new[]
|
|
{
|
|
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewCount() }
|
|
}
|
|
});
|
|
|
|
DebugUI.Table viewTable = new DebugUI.Table
|
|
{
|
|
displayName = "",
|
|
isReadOnly = true
|
|
};
|
|
|
|
// Always add all possible rows, they are dynamically hidden based on actual data
|
|
for (int i = 0; i < k_MaxViewCount; i++)
|
|
viewTable.children.Add(AddInstanceCullerViewDataRow(i));
|
|
|
|
var perViewStats = new DebugUI.Foldout
|
|
{
|
|
displayName = "Per View Stats",
|
|
isHeader = true,
|
|
opened = false,
|
|
isHiddenCallback = () => !data.displayBatcherStats
|
|
};
|
|
perViewStats.children.Add(viewTable);
|
|
|
|
instanceCullerStats.children.Add(perViewStats);
|
|
|
|
|
|
DebugUI.Table eventTable = new DebugUI.Table
|
|
{
|
|
displayName = "", // First column is empty because its content needs to change dynamically
|
|
isReadOnly = true
|
|
};
|
|
|
|
// Always add all possible rows, they are dynamically hidden based on actual data
|
|
for (int i = 0; i < k_MaxOcclusionPassCount; i++)
|
|
eventTable.children.Add(AddInstanceOcclusionPassDataRow(i));
|
|
|
|
var perEventStats = new DebugUI.Foldout
|
|
{
|
|
displayName = "Occlusion Culling Events",
|
|
isHeader = true,
|
|
opened = false,
|
|
isHiddenCallback = () => !data.displayBatcherStats
|
|
};
|
|
perEventStats.children.Add(eventTable);
|
|
|
|
instanceCullerStats.children.Add(perEventStats);
|
|
|
|
|
|
AddWidget(instanceCullerStats);
|
|
}
|
|
private void AddOcclusionContextStatsWidget(DebugDisplayGPUResidentDrawer data)
|
|
{
|
|
var visibilityStats = new DebugUI.Foldout
|
|
{
|
|
displayName = "Occlusion Context Stats",
|
|
isHeader = true,
|
|
opened = true,
|
|
isHiddenCallback = () => !data.occluderContextStats
|
|
};
|
|
|
|
visibilityStats.children.Add(new DebugUI.ValueTuple()
|
|
{
|
|
displayName = "Active Occlusion Contexts",
|
|
values = new[]
|
|
{
|
|
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetOcclusionContextsCounts() }
|
|
}
|
|
});
|
|
|
|
DebugUI.Table viewTable = new DebugUI.Table
|
|
{
|
|
displayName = "", // First column is empty because its content needs to change dynamically
|
|
isReadOnly = true
|
|
};
|
|
|
|
// Always add all possible rows, they are dynamically hidden based on actual data
|
|
for (int i = 0; i < k_MaxContextCount; i++)
|
|
viewTable.children.Add(AddOcclusionContextDataRow(i));
|
|
|
|
visibilityStats.children.Add(viewTable);
|
|
|
|
AddWidget(visibilityStats);
|
|
}
|
|
|
|
}
|
|
|
|
#region IDebugDisplaySettingsQuery
|
|
|
|
/// <inheritdoc/>
|
|
public bool AreAnySettingsActive => displayBatcherStats;
|
|
|
|
/// <inheritdoc/>
|
|
public bool IsPostProcessingAllowed => true;
|
|
|
|
/// <inheritdoc/>
|
|
public bool IsLightingActive => true;
|
|
|
|
/// <inheritdoc/>
|
|
public bool TryGetScreenClearColor(ref Color color)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
IDebugDisplaySettingsPanelDisposable IDebugDisplaySettingsData.CreatePanel()
|
|
{
|
|
return new SettingsPanel(this);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|