using System;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.Universal
{
///
/// Helper class for handling rendering layers.
///
internal static class RenderingLayerUtils
{
public enum Event
{
DepthNormalPrePass,
Opaque,
}
public enum MaskSize
{
Bits8,
Bits16,
Bits24,
Bits32,
}
public static void CombineRendererEvents(bool isDeferred, int msaaSampleCount, Event rendererEvent, ref Event combinedEvent)
{
// Rendering layers can not use MSAA resolve, because it encodes integer
if (msaaSampleCount > 1 && !isDeferred)
combinedEvent = Event.DepthNormalPrePass;
// Otherwise we combine them by selecting the min of the two...
else
combinedEvent = Combine(combinedEvent, rendererEvent);
}
///
/// Returns True if will require rendering layers texture.
///
///
/// List of renderer features used by the renderer
/// MSAA sample count
/// Event at which rendering layers texture needs to be created
/// The mask size of rendering layers texture
public static bool RequireRenderingLayers(UniversalRenderer universalRenderer, List rendererFeatures, int msaaSampleCount, out Event combinedEvent, out MaskSize combinedMaskSize)
{
RenderingMode renderingMode = universalRenderer.renderingModeActual;
bool accurateGBufferNormals = universalRenderer.accurateGbufferNormals;
return RequireRenderingLayers(rendererFeatures, renderingMode, accurateGBufferNormals, msaaSampleCount,
out combinedEvent, out combinedMaskSize);
}
internal static bool RequireRenderingLayers(List rendererFeatures, RenderingMode renderingMode, bool accurateGbufferNormals, int msaaSampleCount, out Event combinedEvent, out MaskSize combinedMaskSize)
{
combinedEvent = Event.Opaque;
combinedMaskSize = MaskSize.Bits8;
bool isDeferred = renderingMode == RenderingMode.Deferred;
bool result = false;
foreach (var rendererFeature in rendererFeatures)
{
if (rendererFeature.isActive)
{
result |= rendererFeature.RequireRenderingLayers(isDeferred, accurateGbufferNormals, out Event rendererEvent, out MaskSize rendererMaskSize);
combinedEvent = Combine(combinedEvent, rendererEvent);
combinedMaskSize = Combine(combinedMaskSize, rendererMaskSize);
}
}
// Rendering layers can not use MSAA resolve, because it encodes integer
if (msaaSampleCount > 1 && combinedEvent == Event.Opaque)
combinedEvent = Event.DepthNormalPrePass;
// Make sure texture has enough bits to encode all rendering layers in urp global settings
if (UniversalRenderPipelineGlobalSettings.instance)
{
int count = RenderingLayerMask.GetRenderingLayerCount();
MaskSize maskSize = GetMaskSize(count);
combinedMaskSize = Combine(combinedMaskSize, maskSize);
}
return result;
}
///
/// Setups properties that are needed for accessing rendering layers texture.
///
/// Used command buffer
/// The mask size of rendering layers texture
public static void SetupProperties(CommandBuffer cmd, MaskSize maskSize) { SetupProperties(CommandBufferHelpers.GetRasterCommandBuffer(cmd), maskSize); }
internal static void SetupProperties(RasterCommandBuffer cmd, MaskSize maskSize)
{
int bits = GetBits(maskSize);
// Pre-computes properties used for packing/unpacking
uint maxInt = bits != 32 ? (1u << bits) - 1u : uint.MaxValue;
float rcpMaxInt = Unity.Mathematics.math.rcp(maxInt);
cmd.SetGlobalInt(ShaderPropertyId.renderingLayerMaxInt, (int)maxInt);
cmd.SetGlobalFloat(ShaderPropertyId.renderingLayerRcpMaxInt, rcpMaxInt);
}
///
/// Converts rendering layers texture mask size to graphics format.
///
public static GraphicsFormat GetFormat(MaskSize maskSize)
{
switch (maskSize)
{
case MaskSize.Bits8:
return GraphicsFormat.R8_UNorm;
case MaskSize.Bits16:
{
//webgpu does not support r16_unorm as a render target format
#if UNITY_2023_2_OR_NEWER
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.WebGPU)
{
return GraphicsFormat.R32_SFloat;
}
#endif
return GraphicsFormat.R16_UNorm;
}
case MaskSize.Bits24:
case MaskSize.Bits32:
return GraphicsFormat.R32_SFloat;
default:
throw new NotImplementedException();
}
}
///
/// Masks rendering layers with those that available in urp global settings.
///
public static uint ToValidRenderingLayers(uint renderingLayers)
{
if (UniversalRenderPipelineGlobalSettings.instance)
{
uint validRenderingLayers = RenderingLayerMask.GetDefinedRenderingLayersCombinedMaskValue();
return validRenderingLayers & renderingLayers;
}
return renderingLayers;
}
static MaskSize GetMaskSize(int bits)
{
int bytes = (bits + 7) / 8;
switch (bytes)
{
case 0:
return MaskSize.Bits8;
case 1:
return MaskSize.Bits8;
case 2:
return MaskSize.Bits16;
case 3:
return MaskSize.Bits24;
case 4:
return MaskSize.Bits32;
default:
return MaskSize.Bits32;
}
}
static int GetBits(MaskSize maskSize)
{
switch (maskSize)
{
case MaskSize.Bits8:
return 8;
case MaskSize.Bits16:
return 16;
case MaskSize.Bits24:
return 24;
case MaskSize.Bits32:
return 32;
default:
throw new NotImplementedException();
}
}
static Event Combine(Event a, Event b)
{
return (Event)Mathf.Min((int)a, (int)b);
}
static MaskSize Combine(MaskSize a, MaskSize b)
{
return (MaskSize)Mathf.Max((int)a, (int)b);
}
}
}