using System;
using System.Runtime.InteropServices;
namespace UnityEngine.Rendering
{
///
/// Variable rate shading lookup table. Use to convert shading rate fragment size and color back and forth.
///
[Serializable]
public class VrsLut
{
///
/// Get a new instance of default VrsLut
///
/// New instance of default VrsLut
public static VrsLut CreateDefault()
{
return new VrsLut()
{
[ShadingRateFragmentSize.FragmentSize1x1] = Color.red,
[ShadingRateFragmentSize.FragmentSize1x2] = Color.yellow,
[ShadingRateFragmentSize.FragmentSize2x1] = Color.white,
[ShadingRateFragmentSize.FragmentSize2x2] = Color.green,
[ShadingRateFragmentSize.FragmentSize1x4] = new Color(0.75f, 0.75f, 0.00f, 1),
[ShadingRateFragmentSize.FragmentSize4x1] = new Color(0.00f, 0.75f, 0.55f, 1),
[ShadingRateFragmentSize.FragmentSize2x4] = new Color(0.50f, 0.00f, 0.50f, 1),
[ShadingRateFragmentSize.FragmentSize4x2] = Color.grey,
[ShadingRateFragmentSize.FragmentSize4x4] = Color.blue,
};
}
[SerializeField]
Color[] m_Data = new Color[Vrs.shadingRateFragmentSizeCount];
///
/// Indexing data with ShadingRateFragmentSize enum.
///
/// Shading rate fragment size to set/get
public Color this[ShadingRateFragmentSize fragmentSize]
{
get => m_Data[(int)fragmentSize];
set => m_Data[(int)fragmentSize] = value;
}
///
/// Create a compute buffer from the lookup table.
///
/// If true, the buffer will be created with the visualization shader in mind
/// Graphics buffer representing this lookup table
public GraphicsBuffer CreateBuffer(bool forVisualization = false)
{
GraphicsBuffer buffer;
Color[] bufferData;
if (forVisualization)
{
// lookup table will be used to map shading rate native values to colors
var fragmentSizes = Enum.GetValues(typeof(ShadingRateFragmentSize));
// Get the encoded binary value associated of the max shading rate supported by our LUT.
// The encoded value will not be sequential. For example, 4x4 is encoded as 0b1010 = 10.
// We do this manually as ShadingRateInfo.QueryNativeValue will return 0 for rates that are
// not supported, which can lead to overflow on devices that support only up to 2x2.
var maxNativeValue = MapFragmentShadingRateToBinary(ShadingRateFragmentSize.FragmentSize4x4);
bufferData = new Color[maxNativeValue + 1];
for (int i = fragmentSizes.Length - 1; i >= 0; --i)
{
var fragmentSize = (ShadingRateFragmentSize)fragmentSizes.GetValue(i);
var nativeValue = ShadingRateInfo.QueryNativeValue(fragmentSize);
bufferData[nativeValue] = m_Data[(int) fragmentSize].linear;
}
}
else
{
// lookup table will be used to map colors to shading rate index
bufferData = new Color[m_Data.Length];
for (int i = 0; i < m_Data.Length; ++i)
{
bufferData[i] = m_Data[i].linear;
}
}
buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, bufferData.Length, Marshal.SizeOf(typeof(Color)));
buffer.SetData(bufferData);
return buffer;
}
private const uint Rate1x = 0;
private const uint Rate2x = 1;
private const uint Rate4x = 2;
private uint MapFragmentShadingRateToBinary(ShadingRateFragmentSize fs)
{
switch (fs)
{
default:
case ShadingRateFragmentSize.FragmentSize1x1:
return EncodeShadingRate(Rate1x, Rate1x);
case ShadingRateFragmentSize.FragmentSize1x2:
return EncodeShadingRate(Rate1x, Rate2x);
case ShadingRateFragmentSize.FragmentSize2x1:
return EncodeShadingRate(Rate2x, Rate1x);
case ShadingRateFragmentSize.FragmentSize2x2:
return EncodeShadingRate(Rate2x, Rate2x);
case ShadingRateFragmentSize.FragmentSize1x4:
return EncodeShadingRate(Rate1x, Rate4x);
case ShadingRateFragmentSize.FragmentSize4x1:
return EncodeShadingRate(Rate4x, Rate1x);
case ShadingRateFragmentSize.FragmentSize2x4:
return EncodeShadingRate(Rate2x, Rate4x);
case ShadingRateFragmentSize.FragmentSize4x2:
return EncodeShadingRate(Rate4x, Rate2x);
case ShadingRateFragmentSize.FragmentSize4x4:
return EncodeShadingRate(Rate4x, Rate4x);
}
}
private uint EncodeShadingRate(uint x, uint y)
{
return ((x << 2) | (y));
}
}
}