using System.Collections.Generic; using System; using System.Reflection; using UnityEngine.Rendering.HighDefinition.Attributes; namespace UnityEngine.Rendering.HighDefinition { namespace Attributes { /// /// Debug View for attributes interpolated from vertex to pixel shader. /// [GenerateHLSL] public enum DebugViewVarying { /// No interpolator debug. None = 0, /// Display texture coordinate 0. Texcoord0 = 1, /// Display texture coordinate 1. Texcoord1, /// Display texture coordinate 2. Texcoord2, /// Display texture coordinate 3. Texcoord3, /// Display tangent in world space. VertexTangentWS, /// Display bi-tangent in world space. VertexBitangentWS, /// Display vertex normal in world space. VertexNormalWS, /// Display vertex color. VertexColor, /// Display vertex color alpha. VertexColorAlpha, // if you add more values here, fix the first entry of next enum }; // Number must be contiguous /// /// Debug view for GBuffers. /// [GenerateHLSL] public enum DebugViewGbuffer { /// No GBuffer debug. None = 0, /// Display GBuffer depth. Depth = DebugViewVarying.VertexColorAlpha + 1, /// Display GBuffer diffuse lighting with albedo and emissive. BakeDiffuseLightingWithAlbedoPlusEmissive, /// Display GBuffer Shadow Mask 0. BakeShadowMask0, /// Display GBuffer Shadow Mask 1. BakeShadowMask1, /// Display GBuffer Shadow Mask 2. BakeShadowMask2, /// Display GBuffer Shadow Mask 3. BakeShadowMask3, // if you add more values here, fix the first entry of next enum } // Number must be contiguous /// /// Debug view for material properties. /// [GenerateHLSL] public enum DebugViewProperties { /// No property debug. None = 0, /// Display materials with tessellation. Tessellation = DebugViewGbuffer.BakeShadowMask3 + 1, /// Display materials with pixel displacement. PixelDisplacement, /// Display materials with vertex displacement. VertexDisplacement, /// Display materials with tessellation displacement. TessellationDisplacement, /// Display materials with depth offset. DepthOffset, /// Display materials with Lightmaps. Lightmap, /// Display materials using instancing. Instancing, } /// /// Display material properties shared between all material types. /// public enum MaterialSharedProperty { /// No shared properties debug. None, /// Display albedo. Albedo, /// Display normal. Normal, /// Display smoothness. Smoothness, /// Display ambient occlusion (N/A for AxF). AmbientOcclusion, /// Display metal (N/A for AxF). Metal, /// Display specular. Specular, /// Display alpha. Alpha, } class MaterialSharedPropertyMappingAttribute : Attribute { public readonly MaterialSharedProperty property; public MaterialSharedPropertyMappingAttribute(MaterialSharedProperty property) => this.property = property; } } /// /// Material Debug Settings. /// [Serializable] public class MaterialDebugSettings { static bool isDebugViewMaterialInit = false; internal static GUIContent[] debugViewMaterialStrings = null; internal static int[] debugViewMaterialValues = null; internal static GUIContent[] debugViewEngineStrings = null; internal static int[] debugViewEngineValues = null; internal static GUIContent[] debugViewMaterialVaryingStrings = null; internal static int[] debugViewMaterialVaryingValues = null; internal static GUIContent[] debugViewMaterialPropertiesStrings = null; internal static int[] debugViewMaterialPropertiesValues = null; internal static GUIContent[] debugViewMaterialTextureStrings = null; internal static int[] debugViewMaterialTextureValues = null; // Had to keep those public because HDRP tests using it (as a workaround to access proper enum values for this debug) /// List of material debug view names. public static GUIContent[] debugViewMaterialGBufferStrings = null; /// List of material debug views values. public static int[] debugViewMaterialGBufferValues = null; static Dictionary s_MaterialPropertyMap = new Dictionary(); /// Current material shared properties debug view. public MaterialSharedProperty debugViewMaterialCommonValue = MaterialSharedProperty.None; static MaterialDebugSettings() { BuildDebugRepresentation(); } // className include the additional "/" static void FillWithProperties(Type type, ref List debugViewMaterialStringsList, ref List debugViewMaterialValuesList, string className) { var attr = type.GetCustomAttribute(); if (!attr.needParamDebug) { return; } var fields = type.GetFields(); var localIndex = 0; foreach (var field in fields) { // Note: One field can have multiple name. This is to allow to have different debug view mode for the same field // like for example display normal in world space or in view space. Same field but two different modes. List displayNames = new List(); if (Attribute.IsDefined(field, typeof(PackingAttribute))) { var packingAttributes = (PackingAttribute[])field.GetCustomAttributes(typeof(PackingAttribute), false); foreach (PackingAttribute packAttr in packingAttributes) { displayNames.AddRange(packAttr.displayNames); } } else { displayNames.Add(field.Name); } // Check if the display name have been override by the users if (Attribute.IsDefined(field, typeof(SurfaceDataAttributes))) { var propertyAttr = (SurfaceDataAttributes[])field.GetCustomAttributes(typeof(SurfaceDataAttributes), false); if (propertyAttr[0].displayNames.Length > 0 && propertyAttr[0].displayNames[0] != "") { displayNames.Clear(); displayNames.AddRange(propertyAttr[0].displayNames); } } foreach (string fieldName in displayNames) { debugViewMaterialStringsList.Add(new GUIContent(className + fieldName)); debugViewMaterialValuesList.Add(attr.paramDefinesStart + (int)localIndex); localIndex++; } } } static void FillWithPropertiesEnum(Type type, ref List debugViewMaterialStringsList, ref List debugViewMaterialValuesList, string prefix) { var names = Enum.GetNames(type); var localIndex = 0; foreach (var value in Enum.GetValues(type)) { var valueName = prefix + names[localIndex]; debugViewMaterialStringsList.Add(new GUIContent(valueName)); debugViewMaterialValuesList.Add((int)value); localIndex++; } } internal class MaterialItem { public String className; public Type surfaceDataType; public Type bsdfDataType; }; static List GetAllMaterialDatas() { List materialList = HDUtils.GetRenderPipelineMaterialList(); // TODO: Share this code to retrieve deferred material with HDRenderPipeline // Find first material that is a deferredMaterial Type bsdfDataDeferredType = null; foreach (RenderPipelineMaterial material in materialList) { if (material.IsDefferedMaterial()) { bsdfDataDeferredType = material.GetType().GetNestedType("BSDFData"); } } // TODO: Handle the case of no Gbuffer material Debug.Assert(bsdfDataDeferredType != null); List materialItems = new List(); int numSurfaceDataFields = 0; int numBSDFDataFields = 0; foreach (RenderPipelineMaterial material in materialList) { MaterialItem item = new MaterialItem(); item.className = material.GetType().Name + "/"; item.surfaceDataType = material.GetType().GetNestedType("SurfaceData"); numSurfaceDataFields += item.surfaceDataType.GetFields().Length; item.bsdfDataType = material.GetType().GetNestedType("BSDFData"); numBSDFDataFields += item.bsdfDataType.GetFields().Length; materialItems.Add(item); } return materialItems; } static void BuildDebugRepresentation() { if (!isDebugViewMaterialInit) { List materialItems = GetAllMaterialDatas(); // Init list List debugViewMaterialStringsList = new List(); List debugViewMaterialValuesList = new List(); List debugViewEngineStringsList = new List(); List debugViewEngineValuesList = new List(); List debugViewMaterialVaryingStringsList = new List(); List debugViewMaterialVaryingValuesList = new List(); List debugViewMaterialPropertiesStringsList = new List(); List debugViewMaterialPropertiesValuesList = new List(); List debugViewMaterialTextureStringsList = new List(); List debugViewMaterialTextureValuesList = new List(); List debugViewMaterialGBufferStringsList = new List(); List debugViewMaterialGBufferValuesList = new List(); // First element is a reserved location and should not be used (allow to track error) // Special case for None since it cannot be inferred from SurfaceData/BuiltinData debugViewMaterialStringsList.Add(new GUIContent("None")); debugViewMaterialValuesList.Add(0); foreach (MaterialItem item in materialItems) { // BuiltinData are duplicated for each material // Giving the material specific types allow to move iterator at a separate range for each material // Otherwise, all BuiltinData will be at same offset and will broke the enum FillWithProperties(typeof(Builtin.BuiltinData), ref debugViewMaterialStringsList, ref debugViewMaterialValuesList, item.className); FillWithProperties(item.surfaceDataType, ref debugViewMaterialStringsList, ref debugViewMaterialValuesList, item.className); } // Engine properties debug // First element is a reserved location and should not be used (allow to track error) // Special case for None since it cannot be inferred from SurfaceData/BuiltinData debugViewEngineStringsList.Add(new GUIContent("None")); debugViewEngineValuesList.Add(0); foreach (MaterialItem item in materialItems) { FillWithProperties(item.bsdfDataType, ref debugViewEngineStringsList, ref debugViewEngineValuesList, item.className); } // For the following, no need to reserve the 0 case as it is handled in the Enum // Attributes debug FillWithPropertiesEnum(typeof(DebugViewVarying), ref debugViewMaterialVaryingStringsList, ref debugViewMaterialVaryingValuesList, ""); // Properties debug FillWithPropertiesEnum(typeof(DebugViewProperties), ref debugViewMaterialPropertiesStringsList, ref debugViewMaterialPropertiesValuesList, ""); // Gbuffer debug FillWithPropertiesEnum(typeof(DebugViewGbuffer), ref debugViewMaterialGBufferStringsList, ref debugViewMaterialGBufferValuesList, ""); FillWithProperties(typeof(Lit.BSDFData), ref debugViewMaterialGBufferStringsList, ref debugViewMaterialGBufferValuesList, ""); // Convert to array for UI debugViewMaterialStrings = debugViewMaterialStringsList.ToArray(); debugViewMaterialValues = debugViewMaterialValuesList.ToArray(); debugViewEngineStrings = debugViewEngineStringsList.ToArray(); debugViewEngineValues = debugViewEngineValuesList.ToArray(); debugViewMaterialVaryingStrings = debugViewMaterialVaryingStringsList.ToArray(); debugViewMaterialVaryingValues = debugViewMaterialVaryingValuesList.ToArray(); debugViewMaterialPropertiesStrings = debugViewMaterialPropertiesStringsList.ToArray(); debugViewMaterialPropertiesValues = debugViewMaterialPropertiesValuesList.ToArray(); debugViewMaterialTextureStrings = debugViewMaterialTextureStringsList.ToArray(); debugViewMaterialTextureValues = debugViewMaterialTextureValuesList.ToArray(); debugViewMaterialGBufferStrings = debugViewMaterialGBufferStringsList.ToArray(); debugViewMaterialGBufferValues = debugViewMaterialGBufferValuesList.ToArray(); //map parameters Dictionary> materialPropertyMap = new Dictionary>() { { MaterialSharedProperty.Albedo, new List() }, { MaterialSharedProperty.Normal, new List() }, { MaterialSharedProperty.Smoothness, new List() }, { MaterialSharedProperty.AmbientOcclusion, new List() }, { MaterialSharedProperty.Metal, new List() }, { MaterialSharedProperty.Specular, new List() }, { MaterialSharedProperty.Alpha, new List() }, }; // builtins parameters Type builtin = typeof(Builtin.BuiltinData); var generateHLSLAttribute = builtin.GetCustomAttribute(); int materialStartIndex = generateHLSLAttribute.paramDefinesStart; int localIndex = 0; foreach (var field in typeof(Builtin.BuiltinData).GetFields()) { if (Attribute.IsDefined(field, typeof(MaterialSharedPropertyMappingAttribute))) { var propertyAttr = (MaterialSharedPropertyMappingAttribute[])field.GetCustomAttributes(typeof(MaterialSharedPropertyMappingAttribute), false); materialPropertyMap[propertyAttr[0].property].Add(materialStartIndex + localIndex); } var surfaceAttributes = (SurfaceDataAttributes[])field.GetCustomAttributes(typeof(SurfaceDataAttributes), false); if (surfaceAttributes.Length > 0) localIndex += surfaceAttributes[0].displayNames.Length; } // specific shader parameters foreach (MaterialItem materialItem in materialItems) { generateHLSLAttribute = materialItem.surfaceDataType.GetCustomAttribute(); materialStartIndex = generateHLSLAttribute.paramDefinesStart; if (!generateHLSLAttribute.needParamDebug) continue; var fields = materialItem.surfaceDataType.GetFields(); localIndex = 0; foreach (var field in fields) { if (Attribute.IsDefined(field, typeof(MaterialSharedPropertyMappingAttribute))) { var propertyAttr = (MaterialSharedPropertyMappingAttribute[])field.GetCustomAttributes(typeof(MaterialSharedPropertyMappingAttribute), false); materialPropertyMap[propertyAttr[0].property].Add(materialStartIndex + localIndex); } var surfaceAttributes = (SurfaceDataAttributes[])field.GetCustomAttributes(typeof(SurfaceDataAttributes), false); if (surfaceAttributes.Length > 0) localIndex += surfaceAttributes[0].displayNames.Length; } if (materialItem.bsdfDataType == null) continue; generateHLSLAttribute = materialItem.bsdfDataType.GetCustomAttribute(); materialStartIndex = generateHLSLAttribute.paramDefinesStart; if (!generateHLSLAttribute.needParamDebug) continue; fields = materialItem.bsdfDataType.GetFields(); localIndex = 0; foreach (var field in fields) { if (Attribute.IsDefined(field, typeof(MaterialSharedPropertyMappingAttribute))) { var propertyAttr = (MaterialSharedPropertyMappingAttribute[])field.GetCustomAttributes(typeof(MaterialSharedPropertyMappingAttribute), false); materialPropertyMap[propertyAttr[0].property].Add(materialStartIndex + localIndex++); } var surfaceAttributes = (SurfaceDataAttributes[])field.GetCustomAttributes(typeof(SurfaceDataAttributes), false); if (surfaceAttributes.Length > 0) localIndex += surfaceAttributes[0].displayNames.Length; } } foreach (var key in materialPropertyMap.Keys) { s_MaterialPropertyMap[key] = materialPropertyMap[key].ToArray(); } isDebugViewMaterialInit = true; } } //Validator Settings /// Color for displaying materials using an albedo value that is too low. public Color materialValidateLowColor = new Color(1.0f, 0.0f, 0.0f); /// Color for displaying materials using an albedo value that is too high. public Color materialValidateHighColor = new Color(0.0f, 0.0f, 1.0f); /// Color for displaying materials using a true metallic color. public Color materialValidateTrueMetalColor = new Color(1.0f, 1.0f, 0.0f); /// Enable display of materials using a true metallic value. public bool materialValidateTrueMetal = false; /// /// Current Debug View Material. /// public int[] debugViewMaterial { get => m_DebugViewMaterial; internal set { int unconstrainedSize = value?.Length ?? 0; if (unconstrainedSize > kDebugViewMaterialBufferLength) Debug.LogError($"DebugViewMaterialBuffer is cannot handle {unconstrainedSize} elements. Only first {kDebugViewMaterialBufferLength} are kept."); int size = Mathf.Min(kDebugViewMaterialBufferLength, unconstrainedSize); if (size == 0) { m_DebugViewMaterial[0] = 1; m_DebugViewMaterial[1] = 0; } else { m_DebugViewMaterial[0] = size; for (int i = 0; i < size; ++i) { m_DebugViewMaterial[i + 1] = value[i]; } } } } /// Current Engine Debug View. public int debugViewEngine { get { return m_DebugViewEngine; } } /// Current Varying Debug View. public DebugViewVarying debugViewVarying { get { return m_DebugViewVarying; } } /// Current Properties Debug View. public DebugViewProperties debugViewProperties { get { return m_DebugViewProperties; } } /// Current GBuffer Debug View. public int debugViewGBuffer { get { return m_DebugViewGBuffer; } } const int kDebugViewMaterialBufferLength = 10; //buffer must be in float as there is no SetGlobalIntArray in API static float[] s_DebugViewMaterialOffsetedBuffer = new float[kDebugViewMaterialBufferLength + 1]; //first is used size // Reminder: _DebugViewMaterial[i] // i==0 -> the size used in the buffer // i>0 -> the index used (0 value means nothing) // The index stored in this buffer could either be // - a gBufferIndex (always stored in _DebugViewMaterialArray[1] as only one supported) // - a property index which is different for each kind of material even if reflecting the same thing (see MaterialSharedProperty) int[] m_DebugViewMaterial = new int[kDebugViewMaterialBufferLength + 1]; // No enum there because everything is generated from materials. int m_DebugViewEngine = 0; // No enum there because everything is generated from BSDFData DebugViewVarying m_DebugViewVarying = DebugViewVarying.None; DebugViewProperties m_DebugViewProperties = DebugViewProperties.None; int m_DebugViewGBuffer = 0; // Can't use GBuffer enum here because the values are actually split between this enum and values from Lit.BSDFData internal int materialEnumIndex; internal float[] GetDebugMaterialIndexes() { // This value is used in the shader for the actual debug display. // There is only one uniform parameter for that so we just add all of them // They are all mutually exclusive so return the sum will return the right index. int size = m_DebugViewMaterial[0]; s_DebugViewMaterialOffsetedBuffer[0] = size; for (int i = 1; i <= size; ++i) { s_DebugViewMaterialOffsetedBuffer[i] = m_DebugViewGBuffer + m_DebugViewMaterial[i] + m_DebugViewEngine + (int)m_DebugViewVarying + (int)m_DebugViewProperties; } return s_DebugViewMaterialOffsetedBuffer; } /// /// Disable all current material debug views. /// public void DisableMaterialDebug() { debugViewMaterialCommonValue = MaterialSharedProperty.None; m_DebugViewMaterial[0] = 1; m_DebugViewMaterial[1] = 0; m_DebugViewEngine = 0; m_DebugViewVarying = DebugViewVarying.None; m_DebugViewProperties = DebugViewProperties.None; m_DebugViewGBuffer = 0; } /// /// Set the current shared material properties debug view. /// /// Desired shared material property to display. public void SetDebugViewCommonMaterialProperty(MaterialSharedProperty value) { if (value != 0) { DisableMaterialDebug(); materialEnumIndex = 0; } debugViewMaterial = value == MaterialSharedProperty.None ? null : s_MaterialPropertyMap[value]; } /// /// Set the current material debug view. /// /// Desired material debug view. public void SetDebugViewMaterial(int value) { debugViewMaterialCommonValue = MaterialSharedProperty.None; if (value != 0) { DisableMaterialDebug(); m_DebugViewMaterial[0] = 1; m_DebugViewMaterial[1] = value; } else { m_DebugViewMaterial[0] = 1; m_DebugViewMaterial[1] = 0; } } /// /// Set the current engine debug view. /// /// Desired engine debug view. public void SetDebugViewEngine(int value) { if (value != 0) DisableMaterialDebug(); m_DebugViewEngine = value; } /// /// Set current varying debug view. /// /// Desired varying debug view. public void SetDebugViewVarying(DebugViewVarying value) { if (value != 0) DisableMaterialDebug(); m_DebugViewVarying = value; } /// /// Set the current Material Property debug view. /// /// Desired property debug view. public void SetDebugViewProperties(DebugViewProperties value) { if (value != 0) DisableMaterialDebug(); m_DebugViewProperties = value; } /// /// Set the current GBuffer debug view. /// /// Desired GBuffer debug view. public void SetDebugViewGBuffer(int value) { if (value != 0) DisableMaterialDebug(); m_DebugViewGBuffer = value; } /// /// Returns true if GBuffer debug is enabled. /// /// True if GBuffer debug is enabled. public bool IsDebugGBufferEnabled() => m_DebugViewGBuffer != 0; /// /// Returns true if Material debug is enabled. /// /// True if Material debug is enabled. public bool IsDebugViewMaterialEnabled() { int size = m_DebugViewMaterial ? [0] ?? 0; bool enabled = false; for (int i = 1; i <= size; ++i) { enabled |= m_DebugViewMaterial[i] != 0; } return enabled; } /// /// Returns true if any material debug display is enabled. /// /// True if any material debug display is enabled. public bool IsDebugDisplayEnabled() { return (m_DebugViewEngine != 0 || IsDebugViewMaterialEnabled() || m_DebugViewVarying != DebugViewVarying.None || m_DebugViewProperties != DebugViewProperties.None || IsDebugGBufferEnabled()); } } }