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,235 @@
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
using UnityEngine.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// Implement a multiple buffering for RenderTextures.
/// </summary>
/// <exemple>
/// <code>
/// enum BufferType
/// {
/// Color,
/// Depth
/// }
///
/// void Render()
/// {
/// var camera = GetCamera();
/// var buffers = GetFrameHistoryBuffersFor(camera);
///
/// // Set reference size in case the rendering size changed this frame
/// buffers.SetReferenceSize(
/// GetCameraWidth(camera), GetCameraHeight(camera),
/// GetCameraUseMSAA(camera), GetCameraMSAASamples(camera)
/// );
/// buffers.Swap();
///
/// var currentColor = buffer.GetFrameRT((int)BufferType.Color, 0);
/// if (currentColor == null) // Buffer was not allocated
/// {
/// buffer.AllocBuffer(
/// (int)BufferType.Color, // Color buffer id
/// ColorBufferAllocator, // Custom functor to implement allocation
/// 2 // Use 2 RT for this buffer for double buffering
/// );
/// currentColor = buffer.GetFrameRT((int)BufferType.Color, 0);
/// }
///
/// var previousColor = buffers.GetFrameRT((int)BufferType.Color, 1);
///
/// // Use previousColor and write into currentColor
/// }
/// </code>
/// </exemple>
public class BufferedRTHandleSystem : IDisposable
{
Dictionary<int, RTHandle[]> m_RTHandles = new Dictionary<int, RTHandle[]>();
RTHandleSystem m_RTHandleSystem = new RTHandleSystem();
bool m_DisposedValue = false;
/// <summary>
/// Maximum allocated width of the Buffered RTHandle System
/// </summary>
public int maxWidth { get { return m_RTHandleSystem.GetMaxWidth(); } }
/// <summary>
/// Maximum allocated height of the Buffered RTHandle System
/// </summary>
public int maxHeight { get { return m_RTHandleSystem.GetMaxHeight(); } }
/// <summary>
/// Current properties of the Buffered RTHandle System
/// </summary>
public RTHandleProperties rtHandleProperties { get { return m_RTHandleSystem.rtHandleProperties; } }
/// <summary>
/// Return the frame RT or null.
/// </summary>
/// <param name="bufferId">Defines the buffer to use.</param>
/// <param name="frameIndex"></param>
/// <returns>The frame RT or null when the <paramref name="bufferId"/> was not previously allocated (<see cref="BufferedRTHandleSystem.AllocBuffer(int, Func{RTHandleSystem, int, RTHandle}, int)" />).</returns>
public RTHandle GetFrameRT(int bufferId, int frameIndex)
{
if (!m_RTHandles.ContainsKey(bufferId))
return null;
Assert.IsTrue(frameIndex >= 0 && frameIndex < m_RTHandles[bufferId].Length);
return m_RTHandles[bufferId][frameIndex];
}
/// <summary>
/// Allocate RT handles for a buffer.
/// </summary>
/// <param name="bufferId">The buffer to allocate.</param>
/// <param name="allocator">The functor to use for allocation.</param>
/// <param name="bufferCount">The number of RT handles for this buffer.</param>
public void AllocBuffer(
int bufferId,
Func<RTHandleSystem, int, RTHandle> allocator,
int bufferCount
)
{
// This function should only be used when there is a non-zero number of buffers to allocate.
// If the caller provides a value of zero, they're likely doing something unintentional in the calling code.
Debug.Assert(bufferCount > 0);
var buffer = new RTHandle[bufferCount];
m_RTHandles.Add(bufferId, buffer);
// First is autoresized
buffer[0] = allocator(m_RTHandleSystem, 0);
// Other are resized on demand
for (int i = 1, c = buffer.Length; i < c; ++i)
{
buffer[i] = allocator(m_RTHandleSystem, i);
m_RTHandleSystem.SwitchResizeMode(buffer[i], RTHandleSystem.ResizeMode.OnDemand);
}
}
/// <summary>
/// Release a buffer
/// </summary>
/// <param name="bufferId">Id of the buffer that needs to be released.</param>
public void ReleaseBuffer(int bufferId)
{
if (m_RTHandles.TryGetValue(bufferId, out var buffers))
{
foreach (var rt in buffers)
m_RTHandleSystem.Release(rt);
}
m_RTHandles.Remove(bufferId);
}
/// <summary>
/// Swap buffers Set the reference size for this RT Handle System (<see cref="RTHandleSystem.SetReferenceSize(int, int, bool)"/>)
/// </summary>
/// <param name="width">The width of the RTs of this buffer.</param>
/// <param name="height">The height of the RTs of this buffer.</param>
public void SwapAndSetReferenceSize(int width, int height)
{
Swap();
m_RTHandleSystem.SetReferenceSize(width, height);
}
/// <summary>
/// Reset the reference size of the system and reallocate all textures.
/// </summary>
/// <param name="width">New width.</param>
/// <param name="height">New height.</param>
public void ResetReferenceSize(int width, int height)
{
m_RTHandleSystem.ResetReferenceSize(width, height);
}
/// <summary>
/// Queries the number of RT handle buffers allocated for a buffer ID.
/// </summary>
/// <param name="bufferId">The buffer ID to query.</param>
/// <returns>The num of frames allocated</returns>
public int GetNumFramesAllocated(int bufferId)
{
if (!m_RTHandles.ContainsKey(bufferId))
return 0;
return m_RTHandles[bufferId].Length;
}
/// <summary>
/// Returns the ratio against the current target's max resolution
/// </summary>
/// <param name="width">width to utilize</param>
/// <param name="height">height to utilize</param>
/// <returns> retruns the width,height / maxTargetSize.xy ratio. </returns>
public Vector2 CalculateRatioAgainstMaxSize(int width, int height)
{
return m_RTHandleSystem.CalculateRatioAgainstMaxSize(new Vector2Int(width, height));
}
void Swap()
{
foreach (var item in m_RTHandles)
{
// Do not index out of bounds...
if (item.Value.Length > 1)
{
var nextFirst = item.Value[item.Value.Length - 1];
for (int i = 0, c = item.Value.Length - 1; i < c; ++i)
item.Value[i + 1] = item.Value[i];
item.Value[0] = nextFirst;
// First is autoresize, other are on demand
m_RTHandleSystem.SwitchResizeMode(item.Value[0], RTHandleSystem.ResizeMode.Auto);
m_RTHandleSystem.SwitchResizeMode(item.Value[1], RTHandleSystem.ResizeMode.OnDemand);
}
else
{
m_RTHandleSystem.SwitchResizeMode(item.Value[0], RTHandleSystem.ResizeMode.Auto);
}
}
}
void Dispose(bool disposing)
{
if (!m_DisposedValue)
{
if (disposing)
{
ReleaseAll();
m_RTHandleSystem.Dispose();
m_RTHandleSystem = null;
}
m_DisposedValue = true;
}
}
/// <summary>
/// Dispose implementation
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// Deallocate and clear all buffers.
/// </summary>
public void ReleaseAll()
{
foreach (var item in m_RTHandles)
{
for (int i = 0, c = item.Value.Length; i < c; ++i)
{
m_RTHandleSystem.Release(item.Value[i]);
}
}
m_RTHandles.Clear();
}
}
}

View File

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

View File

@@ -0,0 +1,20 @@
namespace UnityEngine.Rendering
{
/// <summary>
/// Bit depths of a Depth render texture.
/// Some values may not be supported on all platforms.
/// </summary>
public enum DepthBits
{
/// <summary>No Depth Buffer.</summary>
None = 0,
/// <summary>8 bits Depth Buffer.</summary>
Depth8 = 8,
/// <summary>16 bits Depth Buffer.</summary>
Depth16 = 16,
/// <summary>24 bits Depth Buffer.</summary>
Depth24 = 24,
/// <summary>32 bits Depth Buffer.</summary>
Depth32 = 32
}
}

View File

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

View File

@@ -0,0 +1,17 @@
namespace UnityEngine.Rendering
{
/// <summary>
/// Number of MSAA samples.
/// </summary>
public enum MSAASamples
{
/// <summary>No MSAA.</summary>
None = 1,
/// <summary>MSAA 2X.</summary>
MSAA2x = 2,
/// <summary>MSAA 4X.</summary>
MSAA4x = 4,
/// <summary>MSAA 8X.</summary>
MSAA8x = 8
}
}

View File

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

View File

@@ -0,0 +1,339 @@
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// Texture atlas with rectangular power of two size.
/// </summary>
public class PowerOfTwoTextureAtlas : Texture2DAtlas
{
readonly int m_MipPadding;
const float k_MipmapFactorApprox = 1.33f;
private Dictionary<int, Vector2Int> m_RequestedTextures = new Dictionary<int, Vector2Int>();
/// <summary>
/// Create a new texture atlas, must have power of two size.
/// </summary>
/// <param name="size">The size of the atlas in pixels. Must be power of two.</param>
/// <param name="mipPadding">Amount of mip padding in power of two.</param>
/// <param name="format">Atlas texture format</param>
/// <param name="filterMode">Atlas texture filter mode.</param>
/// <param name="name">Name of the atlas</param>
/// <param name="useMipMap">Use mip maps</param>
public PowerOfTwoTextureAtlas(int size, int mipPadding, GraphicsFormat format, FilterMode filterMode = FilterMode.Point, string name = "", bool useMipMap = true)
: base(size, size, format, filterMode, true, name, useMipMap)
{
this.m_MipPadding = mipPadding;
// Check if size is a power of two
if ((size & (size - 1)) != 0)
Debug.Assert(false, "Power of two atlas was constructed with non power of two size: " + size);
}
/// <summary>
/// Used mipmap padding size in power of two.
/// </summary>
public int mipPadding => m_MipPadding;
int GetTexturePadding() => (int)Mathf.Pow(2, m_MipPadding) * 2;
/// <summary>
/// Get location of the actual texture data without padding in the atlas.
/// </summary>
/// <param name="texture">The source texture cached in the atlas.</param>
/// <param name="scaleOffset">Cached atlas location (scale and offset) for the source texture.</param>
/// <returns>Scale and offset for the source texture without padding.</returns>
public Vector4 GetPayloadScaleOffset(Texture texture, in Vector4 scaleOffset)
{
int pixelPadding = GetTexturePadding();
Vector2 paddingSize = Vector2.one * pixelPadding;
Vector2 textureSize = GetPowerOfTwoTextureSize(texture);
return GetPayloadScaleOffset(textureSize, paddingSize, scaleOffset);
}
/// <summary>
/// Get location of the actual texture data without padding in the atlas.
/// </summary>
/// <param name="textureSize">Size of the source texture</param>
/// <param name="paddingSize">Padding size used for the source texture. </param>
/// <param name="scaleOffset">Cached atlas location (scale and offset) for the source texture.</param>
/// <returns>Scale and offset for the source texture without padding.</returns>
static public Vector4 GetPayloadScaleOffset(in Vector2 textureSize, in Vector2 paddingSize, in Vector4 scaleOffset)
{
// Scale, Offset is a padded atlas sub-texture rectangle.
// Actual texture data (payload) is inset, i.e. padded inwards.
Vector2 subTexScale = new Vector2(scaleOffset.x, scaleOffset.y);
Vector2 subTexOffset = new Vector2(scaleOffset.z, scaleOffset.w);
// NOTE: Should match Blit() padding calculations.
Vector2 scalePadding = ((textureSize + paddingSize) / textureSize); // Size of padding (sampling) rectangle relative to the payload texture.
Vector2 offsetPadding = (paddingSize / 2.0f) / (textureSize + paddingSize); // Padding offset in the padding rectangle
Vector2 insetScale = subTexScale / scalePadding; // Size of payload rectangle in sub-tex
Vector2 insetOffset = subTexOffset + subTexScale * offsetPadding; // Offset of payload rectangle in sub-tex
return new Vector4(insetScale.x, insetScale.y, insetOffset.x, insetOffset.y);
}
private enum BlitType
{
Padding,
PaddingMultiply,
OctahedralPadding,
OctahedralPaddingMultiply,
}
private void Blit2DTexture(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips, BlitType blitType)
{
int mipCount = GetTextureMipmapCount(texture.width, texture.height);
int pixelPadding = GetTexturePadding();
Vector2 textureSize = GetPowerOfTwoTextureSize(texture);
bool bilinear = texture.filterMode != FilterMode.Point;
if (!blitMips)
mipCount = 1;
using (new ProfilingScope(cmd, ProfilingSampler.Get(CoreProfileId.BlitTextureInPotAtlas)))
{
for (int mipLevel = 0; mipLevel < mipCount; mipLevel++)
{
cmd.SetRenderTarget(m_AtlasTexture, mipLevel);
switch (blitType)
{
case BlitType.Padding: Blitter.BlitQuadWithPadding(cmd, texture, textureSize, sourceScaleOffset, scaleOffset, mipLevel, bilinear, pixelPadding); break;
case BlitType.PaddingMultiply: Blitter.BlitQuadWithPaddingMultiply(cmd, texture, textureSize, sourceScaleOffset, scaleOffset, mipLevel, bilinear, pixelPadding); break;
case BlitType.OctahedralPadding: Blitter.BlitOctahedralWithPadding(cmd, texture, textureSize, sourceScaleOffset, scaleOffset, mipLevel, bilinear, pixelPadding); break;
case BlitType.OctahedralPaddingMultiply: Blitter.BlitOctahedralWithPaddingMultiply(cmd, texture, textureSize, sourceScaleOffset, scaleOffset, mipLevel, bilinear, pixelPadding); break;
}
}
}
}
/// <summary>
/// Blit texture into the atlas with padding.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public override void BlitTexture(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips = true, int overrideInstanceID = -1)
{
// We handle ourself the 2D blit because cookies needs mipPadding for trilinear filtering
if (Is2D(texture))
{
Blit2DTexture(cmd, scaleOffset, texture, sourceScaleOffset, blitMips, BlitType.Padding);
MarkGPUTextureValid(overrideInstanceID != -1 ? overrideInstanceID : texture.GetInstanceID(), blitMips);
}
}
/// <summary>
/// Blit texture into the atlas with padding and blending.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public void BlitTextureMultiply(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips = true, int overrideInstanceID = -1)
{
// We handle ourself the 2D blit because cookies needs mipPadding for trilinear filtering
if (Is2D(texture))
{
Blit2DTexture(cmd, scaleOffset, texture, sourceScaleOffset, blitMips, BlitType.PaddingMultiply);
MarkGPUTextureValid(overrideInstanceID != -1 ? overrideInstanceID : texture.GetInstanceID(), blitMips);
}
}
/// <summary>
/// Blit octahedral texture into the atlas with padding.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public override void BlitOctahedralTexture(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips = true, int overrideInstanceID = -1)
{
// We handle ourself the 2D blit because cookies needs mipPadding for trilinear filtering
if (Is2D(texture))
{
Blit2DTexture(cmd, scaleOffset, texture, sourceScaleOffset, blitMips, BlitType.OctahedralPadding);
MarkGPUTextureValid(overrideInstanceID != -1 ? overrideInstanceID : texture.GetInstanceID(), blitMips);
}
}
/// <summary>
/// Blit octahedral texture into the atlas with padding.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public void BlitOctahedralTextureMultiply(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips = true, int overrideInstanceID = -1)
{
// We handle ourself the 2D blit because cookies needs mipPadding for trilinear filtering
if (Is2D(texture))
{
Blit2DTexture(cmd, scaleOffset, texture, sourceScaleOffset, blitMips, BlitType.OctahedralPaddingMultiply);
MarkGPUTextureValid(overrideInstanceID != -1 ? overrideInstanceID : texture.GetInstanceID(), blitMips);
}
}
void TextureSizeToPowerOfTwo(Texture texture, ref int width, ref int height)
{
// Change the width and height of the texture to be power of two
width = Mathf.NextPowerOfTwo(width);
height = Mathf.NextPowerOfTwo(height);
}
Vector2 GetPowerOfTwoTextureSize(Texture texture)
{
int width = texture.width, height = texture.height;
TextureSizeToPowerOfTwo(texture, ref width, ref height);
return new Vector2(width, height);
}
// Override the behavior when we add a texture so all non-pot textures are blitted to a pot target zone
/// <summary>
/// Allocate space from the atlas for a texture and copy texture contents into the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Allocated scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="width">Request width in pixels.</param>
/// <param name="height">Request height in pixels.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
/// <returns>True on success, false otherwise.</returns>
public override bool AllocateTexture(CommandBuffer cmd, ref Vector4 scaleOffset, Texture texture, int width, int height, int overrideInstanceID = -1)
{
// This atlas only supports square textures
if (height != width)
{
Debug.LogError("Can't place " + texture + " in the atlas " + m_AtlasTexture.name + ": Only squared texture are allowed in this atlas.");
return false;
}
TextureSizeToPowerOfTwo(texture, ref height, ref width);
return base.AllocateTexture(cmd, ref scaleOffset, texture, width, height);
}
/// <summary>
/// Clear tracked requested textures.
/// </summary>
public void ResetRequestedTexture() => m_RequestedTextures.Clear();
/// <summary>
/// Reserves the space on the texture atlas
/// </summary>
/// <param name="texture">The source texture</param>
/// <returns>True if the space is reserved</returns>
public bool ReserveSpace(Texture texture) => ReserveSpace(texture, texture.width, texture.height);
/// <summary>
/// Reserves the space on the texture atlas
/// </summary>
/// <param name="texture">The source texture</param>
/// <param name="width">The width</param>
/// <param name="height">The height</param>
/// <returns>True if the space is reserved</returns>
public bool ReserveSpace(Texture texture, int width, int height)
=> ReserveSpace(GetTextureID(texture), width, height);
/// <summary>
/// Reserves the space on the texture atlas
/// </summary>
/// <param name="textureA">The source texture A</param>
/// <param name="textureB">The source texture B</param>
/// <param name="width">The width</param>
/// <param name="height">The height</param>
/// <returns>True if the space is reserved</returns>
public bool ReserveSpace(Texture textureA, Texture textureB, int width, int height)
=> ReserveSpace(GetTextureID(textureA, textureB), width, height);
/// <summary>
/// Reserves the space on the texture atlas
/// </summary>
/// <param name="id">The id</param>
/// <param name="width">The width</param>
/// <param name="height">The height</param>
/// <returns>True if the space is reserved</returns>
bool ReserveSpace(int id, int width, int height)
{
m_RequestedTextures[id] = new Vector2Int(width, height);
// Cookie texture resolution changing between frame is a special case, so we handle it here.
// The texture will be re-allocated and may cause holes in the atlas texture, which is fine
// because when it doesn't have any more space, it will re-layout the texture correctly.
var cachedSize = GetCachedTextureSize(id);
if (!IsCached(out _, id) || cachedSize.x != width || cachedSize.y != height)
{
Vector4 scaleBias = Vector4.zero;
if (!AllocateTextureWithoutBlit(id, width, height, ref scaleBias))
return false;
}
return true;
}
/// <summary>
/// sort all the requested allocation from biggest to smallest and re-insert them.
/// This function does not moves the textures in the atlas, it only changes their coordinates
/// </summary>
/// <returns>True if all textures have successfully been re-inserted in the atlas</returns>
public bool RelayoutEntries()
{
var entries = new List<(int instanceId, Vector2Int size)>();
foreach (var entry in m_RequestedTextures)
entries.Add((entry.Key, entry.Value));
ResetAllocator();
// Sort entries from biggest to smallest
entries.Sort((c1, c2) =>
{
return c2.size.magnitude.CompareTo(c1.size.magnitude);
});
bool success = true;
Vector4 newScaleOffset = Vector4.zero;
foreach (var e in entries)
success &= AllocateTextureWithoutBlit(e.instanceId, e.size.x, e.size.y, ref newScaleOffset);
return success;
}
/// <summary>
/// Get cache size in bytes.
/// </summary>
/// <param name="nbElement"></param>
/// <param name="resolution">Atlas resolution (square).</param>
/// <param name="hasMipmap">Atlas uses mip maps.</param>
/// <param name="format">Atlas format.</param>
/// <returns></returns>
public static long GetApproxCacheSizeInByte(int nbElement, int resolution, bool hasMipmap, GraphicsFormat format)
=> (long)(nbElement * resolution * resolution * (double)((hasMipmap ? k_MipmapFactorApprox : 1.0f) * GraphicsFormatUtility.GetBlockSize(format)));
/// <summary>
/// Compute the max size of a power of two atlas for a given size in byte (weight).
/// </summary>
/// <param name="weight">Atlas size in bytes.</param>
/// <param name="hasMipmap">Atlas uses mip maps.</param>
/// <param name="format">Atlas format.</param>
/// <returns></returns>
public static int GetMaxCacheSizeForWeightInByte(int weight, bool hasMipmap, GraphicsFormat format)
{
float bytePerPixel = (float)GraphicsFormatUtility.GetBlockSize(format) * (hasMipmap ? k_MipmapFactorApprox : 1.0f);
var maxAtlasSquareSize = Mathf.Sqrt((float)weight / bytePerPixel);
return CoreUtils.PreviousPowerOfTwo((int)maxAtlasSquareSize);
}
}
}

View File

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

View File

@@ -0,0 +1,311 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// This struct contains some static helpers that can be used when converting RTid to RThandle
/// The common use case is to convert rtId to rtHandle and use the handle with other handle compatible core APIs
/// </summary>
public struct RTHandleStaticHelpers
{
/// <summary>
/// Static RTHandle wrapper around RenderTargetIdentifier to avoid gc.alloc
/// Set this wrapper through `RTHandleStaticHelpers.SetRTHandleStaticWrapper`
/// </summary>
public static RTHandle s_RTHandleWrapper;
/// <summary>
/// Set static RTHandle wrapper given a RTid. The static RTHandle wrapper is treated as external handle in RTHandleSystem
/// Get the static wrapper through `RTHandleStaticHelpers.s_RTHandleWrapper`.
/// </summary>
/// <param name="rtId">Input render target identifier to be converted.</param>
public static void SetRTHandleStaticWrapper(RenderTargetIdentifier rtId)
{
if (s_RTHandleWrapper == null)
s_RTHandleWrapper = RTHandles.Alloc(rtId);
else
s_RTHandleWrapper.SetTexture(rtId);
}
/// <summary>
/// Set user managed RTHandle wrapper given a RTid. The wrapper is treated as external handle in RTHandleSystem
/// </summary>
/// <param name="rtWrapper">User managed RTHandle wrapper.</param>
/// <param name="rtId">Input render target identifier to be set.</param>
public static void SetRTHandleUserManagedWrapper(ref RTHandle rtWrapper, RenderTargetIdentifier rtId)
{
// User managed wrapper is null, just return here.
if (rtWrapper == null)
return;
// Check user managed RTHandle wrapper is actually a warpper around RTid
if (rtWrapper.m_RT != null)
throw new ArgumentException($"Input wrapper must be a wrapper around RenderTargetIdentifier. Passed in warpper contains valid RenderTexture {rtWrapper.m_RT.name} and cannot be used as warpper.");
if (rtWrapper.m_ExternalTexture != null)
throw new ArgumentException($"Input wrapper must be a wrapper around RenderTargetIdentifier. Passed in warpper contains valid Texture {rtWrapper.m_ExternalTexture.name} and cannot be used as warpper.");
rtWrapper.SetTexture(rtId);
}
}
/// <summary>
/// A RTHandle is a RenderTexture that scales automatically with the camera size.
/// This allows proper reutilization of RenderTexture memory when different cameras with various sizes are used during rendering.
/// <seealso cref="RTHandleSystem"/>
/// </summary>
public class RTHandle
{
internal RTHandleSystem m_Owner;
internal RenderTexture m_RT;
internal Texture m_ExternalTexture;
internal RenderTargetIdentifier m_NameID;
internal bool m_EnableMSAA = false;
internal bool m_EnableRandomWrite = false;
internal bool m_EnableHWDynamicScale = false;
internal string m_Name;
internal bool m_UseCustomHandleScales = false;
internal RTHandleProperties m_CustomHandleProperties;
/// <summary>
/// By default, rtHandleProperties gets the global state of scalers against the global reference mode.
/// This method lets the current RTHandle use a local custom RTHandleProperties. This function is being used
/// by scalers such as TAAU and DLSS, which require to have a different resolution for color (independent of the RTHandleSystem).
/// </summary>
/// <param name="properties">Properties to set.</param>
public void SetCustomHandleProperties(in RTHandleProperties properties)
{
m_UseCustomHandleScales = true;
m_CustomHandleProperties = properties;
}
/// <summary>
/// Method that clears any custom handle property being set.
/// </summary>
public void ClearCustomHandleProperties()
{
m_UseCustomHandleScales = false;
}
/// <summary>
/// Scale factor applied to the RTHandle reference size.
/// </summary>
public Vector2 scaleFactor { get; internal set; }
internal ScaleFunc scaleFunc;
/// <summary>
/// Returns true if the RTHandle uses automatic scaling.
/// </summary>
public bool useScaling { get; internal set; }
/// <summary>
/// Reference size of the RTHandle System associated with the RTHandle
/// </summary>
public Vector2Int referenceSize { get; internal set; }
/// <summary>
/// Current properties of the RTHandle System. If a custom property has been set through SetCustomHandleProperties method, it will be used that one instead.
/// </summary>
public RTHandleProperties rtHandleProperties { get { return m_UseCustomHandleScales ? m_CustomHandleProperties : m_Owner.rtHandleProperties; } }
/// <summary>
/// RenderTexture associated with the RTHandle
/// </summary>
public RenderTexture rt { get { return m_RT; } }
/// <summary>
/// RenderTargetIdentifier associated with the RTHandle
/// </summary>
public RenderTargetIdentifier nameID { get { return m_NameID; } }
/// <summary>
/// Name of the RTHandle
/// </summary>
public string name { get { return m_Name; } }
/// <summary>
/// Returns true is MSAA is enabled, false otherwise.
/// </summary>
public bool isMSAAEnabled { get { return m_EnableMSAA; } }
// Keep constructor private
internal RTHandle(RTHandleSystem owner)
{
m_Owner = owner;
}
/// <summary>
/// Implicit conversion operator to RenderTargetIdentifier
/// </summary>
/// <param name="handle">Input RTHandle</param>
/// <returns>RenderTargetIdentifier representation of the RTHandle.</returns>
public static implicit operator RenderTargetIdentifier(RTHandle handle)
{
return handle != null ? handle.nameID : default(RenderTargetIdentifier);
}
/// <summary>
/// Implicit conversion operator to Texture
/// </summary>
/// <param name="handle">Input RTHandle</param>
/// <returns>Texture representation of the RTHandle.</returns>
public static implicit operator Texture(RTHandle handle)
{
// If RTHandle is null then conversion should give a null Texture
if (handle == null)
return null;
Debug.Assert(handle.m_ExternalTexture != null || handle.rt != null);
return (handle.rt != null) ? handle.rt : handle.m_ExternalTexture;
}
/// <summary>
/// Implicit conversion operator to RenderTexture
/// </summary>
/// <param name="handle">Input RTHandle</param>
/// <returns>RenderTexture representation of the RTHandle.</returns>
public static implicit operator RenderTexture(RTHandle handle)
{
// If RTHandle is null then conversion should give a null RenderTexture
if (handle == null)
return null;
Debug.Assert(handle.rt != null, "RTHandle was created using a regular Texture and is used as a RenderTexture");
return handle.rt;
}
internal void SetRenderTexture(RenderTexture rt)
{
m_RT = rt;
m_ExternalTexture = null;
m_NameID = new RenderTargetIdentifier(rt);
}
internal void SetTexture(Texture tex)
{
m_RT = null;
m_ExternalTexture = tex;
m_NameID = new RenderTargetIdentifier(tex);
}
internal void SetTexture(RenderTargetIdentifier tex)
{
m_RT = null;
m_ExternalTexture = null;
m_NameID = tex;
}
/// <summary>
/// Get the Instance ID of the RTHandle.
/// </summary>
/// <returns>The RTHandle Instance ID.</returns>
public int GetInstanceID()
{
if (m_RT != null)
return m_RT.GetInstanceID();
else if (m_ExternalTexture != null)
return m_ExternalTexture.GetInstanceID();
else
return m_NameID.GetHashCode(); // No instance ID so we return the hash code.
}
/// <summary>
/// Release the RTHandle
/// </summary>
public void Release()
{
m_Owner.Remove(this);
CoreUtils.Destroy(m_RT);
m_NameID = BuiltinRenderTextureType.None;
m_RT = null;
m_ExternalTexture = null;
}
/// <summary>
/// Return the input size, scaled by the RTHandle scale factor.
/// </summary>
/// <param name="refSize">Input size</param>
/// <returns>Input size scaled by the RTHandle scale factor.</returns>
public Vector2Int GetScaledSize(Vector2Int refSize)
{
if (!useScaling)
return refSize;
if (scaleFunc != null)
{
return scaleFunc(refSize);
}
else
{
return new Vector2Int(
x: Mathf.RoundToInt(scaleFactor.x * refSize.x),
y: Mathf.RoundToInt(scaleFactor.y * refSize.y)
);
}
}
/// <summary>
/// Return the scaled size of the RTHandle.
/// </summary>
/// <returns>The scaled size of the RTHandle.</returns>
public Vector2Int GetScaledSize()
{
if (!useScaling)
return referenceSize;
if (scaleFunc != null)
{
return scaleFunc(referenceSize);
}
else
{
return new Vector2Int(
x: Mathf.RoundToInt(scaleFactor.x * referenceSize.x),
y: Mathf.RoundToInt(scaleFactor.y * referenceSize.y)
);
}
}
#if UNITY_2020_2_OR_NEWER
/// <summary>
/// Switch the render target to fast memory on platform that have it.
/// </summary>
/// <param name="cmd">Command buffer used for rendering.</param>
/// <param name="residencyFraction">How much of the render target is to be switched into fast memory (between 0 and 1).</param>
/// <param name="flags">Flag to determine what parts of the render target is spilled if not fully resident in fast memory.</param>
/// <param name="copyContents">Whether the content of render target are copied or not when switching to fast memory.</param>
public void SwitchToFastMemory(CommandBuffer cmd,
float residencyFraction = 1.0f,
FastMemoryFlags flags = FastMemoryFlags.SpillTop,
bool copyContents = false
)
{
residencyFraction = Mathf.Clamp01(residencyFraction);
cmd.SwitchIntoFastMemory(m_RT, flags, residencyFraction, copyContents);
}
/// <summary>
/// Switch the render target to fast memory on platform that have it and copies the content.
/// </summary>
/// <param name="cmd">Command buffer used for rendering.</param>
/// <param name="residencyFraction">How much of the render target is to be switched into fast memory (between 0 and 1).</param>
/// <param name="flags">Flag to determine what parts of the render target is spilled if not fully resident in fast memory.</param>
public void CopyToFastMemory(CommandBuffer cmd,
float residencyFraction = 1.0f,
FastMemoryFlags flags = FastMemoryFlags.SpillTop
)
{
SwitchToFastMemory(cmd, residencyFraction, flags, copyContents: true);
}
/// <summary>
/// Switch out the render target from fast memory back to main memory on platforms that have fast memory.
/// </summary>
/// <param name="cmd">Command buffer used for rendering.</param>
/// <param name="copyContents">Whether the content of render target are copied or not when switching out fast memory.</param>
public void SwitchOutFastMemory(CommandBuffer cmd, bool copyContents = true)
{
cmd.SwitchOutOfFastMemory(m_RT, copyContents);
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,945 @@
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// Scaled function used to compute the size of a RTHandle for the current frame.
/// </summary>
/// <param name="size">Reference size of the RTHandle system for the frame.</param>
/// <returns>The size of the RTHandled computed from the reference size.</returns>
public delegate Vector2Int ScaleFunc(Vector2Int size);
/// <summary>
/// List of properties of the RTHandle System for the current frame.
/// </summary>
public struct RTHandleProperties
{
/// <summary>
/// Size set as reference at the previous frame
/// </summary>
public Vector2Int previousViewportSize;
/// <summary>
/// Size of the render targets at the previous frame
/// </summary>
public Vector2Int previousRenderTargetSize;
/// <summary>
/// Size set as reference at the current frame
/// </summary>
public Vector2Int currentViewportSize;
/// <summary>
/// Size of the render targets at the current frame
/// </summary>
public Vector2Int currentRenderTargetSize;
/// <summary>
/// Scale factor from RTHandleSystem max size to requested reference size (referenceSize/maxSize)
/// (x,y) current frame (z,w) last frame (this is only used for buffered RTHandle Systems)
/// </summary>
public Vector4 rtHandleScale;
}
/// <summary>
/// System managing a set of RTHandle textures
/// </summary>
public partial class RTHandleSystem : IDisposable
{
internal enum ResizeMode
{
Auto,
OnDemand
}
// Parameters for auto-scaled Render Textures
bool m_HardwareDynamicResRequested = false;
HashSet<RTHandle> m_AutoSizedRTs;
RTHandle[] m_AutoSizedRTsArray; // For fast iteration
HashSet<RTHandle> m_ResizeOnDemandRTs;
RTHandleProperties m_RTHandleProperties;
/// <summary>
/// Current properties of the RTHandle System.
/// </summary>
public RTHandleProperties rtHandleProperties { get { return m_RTHandleProperties; } }
int m_MaxWidths = 0;
int m_MaxHeights = 0;
#if UNITY_EDITOR
// In editor every now and then we must reset the size of the rthandle system if it was set very high and then switched back to a much smaller scale.
int m_FramesSinceLastReset = 0;
#endif
/// <summary>
/// RTHandleSystem constructor.
/// </summary>
public RTHandleSystem()
{
m_AutoSizedRTs = new HashSet<RTHandle>();
m_ResizeOnDemandRTs = new HashSet<RTHandle>();
m_MaxWidths = 1;
m_MaxHeights = 1;
}
/// <summary>
/// Disposable pattern implementation
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// Initialize the RTHandle system.
/// </summary>
/// <param name="width">Initial reference rendering width.</param>
/// <param name="height">Initial reference rendering height.</param>
/// <param name="useLegacyDynamicResControl">Use legacy hardware DynamicResolution control in RTHandle system.</param>
public void Initialize(int width, int height, bool useLegacyDynamicResControl = false)
{
if (m_AutoSizedRTs.Count != 0)
{
string leakingResources = "Unreleased RTHandles:";
foreach (var rt in m_AutoSizedRTs)
{
leakingResources = string.Format("{0}\n {1}", leakingResources, rt.name);
}
Debug.LogError(string.Format("RTHandle.Initialize should only be called once before allocating any Render Texture. This may be caused by an unreleased RTHandle resource.\n{0}\n", leakingResources));
}
m_MaxWidths = width;
m_MaxHeights = height;
if (useLegacyDynamicResControl)
m_HardwareDynamicResRequested = true;
else
m_HardwareDynamicResRequested = DynamicResolutionHandler.instance.RequestsHardwareDynamicResolution();
}
/// <summary>
/// Release memory of a RTHandle from the RTHandle System
/// </summary>
/// <param name="rth">RTHandle that should be released.</param>
public void Release(RTHandle rth)
{
if (rth != null)
{
Assert.AreEqual(this, rth.m_Owner);
rth.Release();
}
}
internal void Remove(RTHandle rth)
{
m_AutoSizedRTs.Remove(rth);
}
/// <summary>
/// Reset the reference size of the system and reallocate all textures.
/// </summary>
/// <param name="width">New width.</param>
/// <param name="height">New height.</param>
public void ResetReferenceSize(int width, int height)
{
m_MaxWidths = width;
m_MaxHeights = height;
SetReferenceSize(width, height, reset: true);
}
/// <summary>
/// Sets the reference rendering size for subsequent rendering for the RTHandle System
/// </summary>
/// <param name="width">Reference rendering width for subsequent rendering.</param>
/// <param name="height">Reference rendering height for subsequent rendering.</param>
public void SetReferenceSize(int width, int height)
{
SetReferenceSize(width, height, false);
}
/// <summary>
/// Sets the reference rendering size for subsequent rendering for the RTHandle System
/// </summary>
/// <param name="width">Reference rendering width for subsequent rendering.</param>
/// <param name="height">Reference rendering height for subsequent rendering.</param>
/// <param name="reset">If set to true, the new width and height will override the old values even if they are not bigger.</param>
public void SetReferenceSize(int width, int height, bool reset)
{
m_RTHandleProperties.previousViewportSize = m_RTHandleProperties.currentViewportSize;
m_RTHandleProperties.previousRenderTargetSize = m_RTHandleProperties.currentRenderTargetSize;
Vector2 lastFrameMaxSize = new Vector2(GetMaxWidth(), GetMaxHeight());
width = Mathf.Max(width, 1);
height = Mathf.Max(height, 1);
#if UNITY_EDITOR
// If the reference size is significantly higher than the current actualWidth/Height and it is larger than 1440p dimensions, we reset the reference size every several frames
// in editor to avoid issues if a large resolution was temporarily set.
const int resetInterval = 100;
if (((m_MaxWidths / (float)width) > 2.0f && m_MaxWidths > 2560) ||
((m_MaxHeights / (float)height) > 2.0f && m_MaxHeights > 1440))
{
if (m_FramesSinceLastReset > resetInterval)
{
m_FramesSinceLastReset = 0;
ResetReferenceSize(width, height);
}
m_FramesSinceLastReset++;
}
else
{
// If some cameras is a reasonable resolution size, we dont reset.
m_FramesSinceLastReset = 0;
}
#endif
bool sizeChanged = width > GetMaxWidth() || height > GetMaxHeight() || reset;
if (sizeChanged)
{
Resize(width, height, sizeChanged);
}
m_RTHandleProperties.currentViewportSize = new Vector2Int(width, height);
m_RTHandleProperties.currentRenderTargetSize = new Vector2Int(GetMaxWidth(), GetMaxHeight());
// If the currentViewportSize is 0, it mean we are the first frame of rendering (can happen when doing domain reload for example or for reflection probe)
// in this case the scalePrevious below could be invalided. But some effect rely on having a correct value like TAA with the history buffer for the first frame.
// to work around this, when we detect that size is 0, we setup previous size to current size.
if (m_RTHandleProperties.previousViewportSize.x == 0)
{
m_RTHandleProperties.previousViewportSize = m_RTHandleProperties.currentViewportSize;
m_RTHandleProperties.previousRenderTargetSize = m_RTHandleProperties.currentRenderTargetSize;
lastFrameMaxSize = new Vector2(GetMaxWidth(), GetMaxHeight());
}
var scales = CalculateRatioAgainstMaxSize(m_RTHandleProperties.currentViewportSize);
if (DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled() && m_HardwareDynamicResRequested)
{
// Making the final scale in 'drs' space, since the final scale must account for rounding pixel values.
m_RTHandleProperties.rtHandleScale = new Vector4(scales.x, scales.y, m_RTHandleProperties.rtHandleScale.x, m_RTHandleProperties.rtHandleScale.y);
}
else
{
Vector2 scalePrevious = m_RTHandleProperties.previousViewportSize / lastFrameMaxSize;
m_RTHandleProperties.rtHandleScale = new Vector4(scales.x, scales.y, scalePrevious.x, scalePrevious.y);
}
}
internal Vector2 CalculateRatioAgainstMaxSize(in Vector2Int viewportSize)
{
Vector2 maxSize = new Vector2(GetMaxWidth(), GetMaxHeight());
if (DynamicResolutionHandler.instance.HardwareDynamicResIsEnabled() && m_HardwareDynamicResRequested && viewportSize != DynamicResolutionHandler.instance.finalViewport)
{
//for hardware resolution, the final goal is to figure out a scale from finalViewport into maxViewport.
//This is however wrong! because the actualViewport might not fit the finalViewport perfectly, due to rounding.
//A correct way is to instead downscale the maxViewport, and keep the final scale in terms of downsampled buffers.
Vector2 currentScale = (Vector2)viewportSize / (Vector2)DynamicResolutionHandler.instance.finalViewport;
maxSize = DynamicResolutionHandler.instance.ApplyScalesOnSize(new Vector2Int(GetMaxWidth(), GetMaxHeight()), currentScale);
}
return new Vector2((float)viewportSize.x / maxSize.x, (float)viewportSize.y / maxSize.y);
}
/// <summary>
/// Enable or disable hardware dynamic resolution for the RTHandle System
/// </summary>
/// <param name="enableHWDynamicRes">State of hardware dynamic resolution.</param>
public void SetHardwareDynamicResolutionState(bool enableHWDynamicRes)
{
if (enableHWDynamicRes != m_HardwareDynamicResRequested)
{
m_HardwareDynamicResRequested = enableHWDynamicRes;
Array.Resize(ref m_AutoSizedRTsArray, m_AutoSizedRTs.Count);
m_AutoSizedRTs.CopyTo(m_AutoSizedRTsArray);
for (int i = 0, c = m_AutoSizedRTsArray.Length; i < c; ++i)
{
var rth = m_AutoSizedRTsArray[i];
// Grab the render texture
var renderTexture = rth.m_RT;
if (renderTexture)
{
// Free the previous version
renderTexture.Release();
renderTexture.useDynamicScale = m_HardwareDynamicResRequested && rth.m_EnableHWDynamicScale;
// Create the render texture
renderTexture.Create();
}
}
}
}
internal void SwitchResizeMode(RTHandle rth, ResizeMode mode)
{
// Don't do anything is scaling isn't enabled on this RT
// TODO: useScaling should probably be moved to ResizeMode.Fixed or something
if (!rth.useScaling)
return;
switch (mode)
{
case ResizeMode.OnDemand:
m_AutoSizedRTs.Remove(rth);
m_ResizeOnDemandRTs.Add(rth);
break;
case ResizeMode.Auto:
// Resize now so it is consistent with other auto resize RTHs
if (m_ResizeOnDemandRTs.Contains(rth))
DemandResize(rth);
m_ResizeOnDemandRTs.Remove(rth);
m_AutoSizedRTs.Add(rth);
break;
}
}
void DemandResize(RTHandle rth)
{
Assert.IsTrue(m_ResizeOnDemandRTs.Contains(rth), "The RTHandle is not an resize on demand handle in this RTHandleSystem. Please call SwitchToResizeOnDemand(rth, true) before resizing on demand.");
// Grab the render texture
var rt = rth.m_RT;
rth.referenceSize = new Vector2Int(m_MaxWidths, m_MaxHeights);
var scaledSize = rth.GetScaledSize(rth.referenceSize);
scaledSize = Vector2Int.Max(Vector2Int.one, scaledSize);
// Did the size change?
var sizeChanged = rt.width != scaledSize.x || rt.height != scaledSize.y;
if (sizeChanged)
{
// Free this render texture
rt.Release();
// Update the size
rt.width = scaledSize.x;
rt.height = scaledSize.y;
// Generate a new name
rt.name = CoreUtils.GetRenderTargetAutoName(
rt.width,
rt.height,
rt.volumeDepth,
rt.graphicsFormat,
rt.dimension,
rth.m_Name,
mips: rt.useMipMap,
enableMSAA: rth.m_EnableMSAA,
msaaSamples: (MSAASamples)rt.antiAliasing,
dynamicRes: rt.useDynamicScale
);
// Create the new texture
rt.Create();
}
}
/// <summary>
/// Returns the maximum allocated width of the RTHandle System.
/// </summary>
/// <returns>Maximum allocated width of the RTHandle System.</returns>
public int GetMaxWidth() { return m_MaxWidths; }
/// <summary>
/// Returns the maximum allocated height of the RTHandle System.
/// </summary>
/// <returns>Maximum allocated height of the RTHandle System.</returns>
public int GetMaxHeight() { return m_MaxHeights; }
void Dispose(bool disposing)
{
if (disposing)
{
Array.Resize(ref m_AutoSizedRTsArray, m_AutoSizedRTs.Count);
m_AutoSizedRTs.CopyTo(m_AutoSizedRTsArray);
for (int i = 0, c = m_AutoSizedRTsArray.Length; i < c; ++i)
{
var rt = m_AutoSizedRTsArray[i];
Release(rt);
}
m_AutoSizedRTs.Clear();
Array.Resize(ref m_AutoSizedRTsArray, m_ResizeOnDemandRTs.Count);
m_ResizeOnDemandRTs.CopyTo(m_AutoSizedRTsArray);
for (int i = 0, c = m_AutoSizedRTsArray.Length; i < c; ++i)
{
var rt = m_AutoSizedRTsArray[i];
Release(rt);
}
m_ResizeOnDemandRTs.Clear();
m_AutoSizedRTsArray = null;
}
}
void Resize(int width, int height, bool sizeChanged)
{
m_MaxWidths = Math.Max(width, m_MaxWidths);
m_MaxHeights = Math.Max(height, m_MaxHeights);
var maxSize = new Vector2Int(m_MaxWidths, m_MaxHeights);
Array.Resize(ref m_AutoSizedRTsArray, m_AutoSizedRTs.Count);
m_AutoSizedRTs.CopyTo(m_AutoSizedRTsArray);
for (int i = 0, c = m_AutoSizedRTsArray.Length; i < c; ++i)
{
// Grab the RT Handle
var rth = m_AutoSizedRTsArray[i];
// Force its new reference size
rth.referenceSize = maxSize;
// Grab the render texture
var renderTexture = rth.m_RT;
// Free the previous version
renderTexture.Release();
// Get the scaled size
var scaledSize = rth.GetScaledSize(maxSize);
renderTexture.width = Mathf.Max(scaledSize.x, 1);
renderTexture.height = Mathf.Max(scaledSize.y, 1);
// Regenerate the name
renderTexture.name = CoreUtils.GetRenderTargetAutoName(renderTexture.width, renderTexture.height, renderTexture.volumeDepth, renderTexture.graphicsFormat, renderTexture.dimension, rth.m_Name, mips: renderTexture.useMipMap, enableMSAA: rth.m_EnableMSAA, msaaSamples: (MSAASamples)renderTexture.antiAliasing, dynamicRes: renderTexture.useDynamicScale);
// Create the render texture
renderTexture.Create();
}
}
/// <summary>
/// Allocate a new fixed sized RTHandle.
/// </summary>
/// <param name="width">With of the RTHandle.</param>
/// <param name="height">Heigh of the RTHandle.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples for the RTHandle.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns></returns>
public RTHandle Alloc(
int width,
int height,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0f,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
return Alloc(width, height, wrapMode, wrapMode, wrapMode, slices, depthBufferBits, colorFormat, filterMode, dimension, enableRandomWrite, useMipMap,
autoGenerateMips, isShadowMap, anisoLevel, mipMapBias, msaaSamples, bindTextureMS, useDynamicScale, memoryless, vrUsage, name);
}
/// <summary>
/// Allocate a new fixed sized RTHandle.
/// </summary>
/// <param name="width">With of the RTHandle.</param>
/// <param name="height">Heigh of the RTHandle.</param>
/// <param name="wrapModeU">U coordinate wrapping mode of the RTHandle.</param>
/// <param name="wrapModeV">V coordinate wrapping mode of the RTHandle.</param>
/// <param name="wrapModeW">W coordinate wrapping mode of the RTHandle.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples for the RTHandle.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns></returns>
public RTHandle Alloc(
int width,
int height,
TextureWrapMode wrapModeU,
TextureWrapMode wrapModeV,
TextureWrapMode wrapModeW = TextureWrapMode.Repeat,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0f,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
bool enableMSAA = msaaSamples != MSAASamples.None;
if (!enableMSAA && bindTextureMS == true)
{
Debug.LogWarning("RTHandle allocated without MSAA but with bindMS set to true, forcing bindMS to false.");
bindTextureMS = false;
}
// We need to handle this in an explicit way since GraphicsFormat does not expose depth formats. TODO: Get rid of this branch once GraphicsFormat'll expose depth related formats
RenderTexture rt;
if (isShadowMap || depthBufferBits != DepthBits.None)
{
RenderTextureFormat format = isShadowMap ? RenderTextureFormat.Shadowmap : RenderTextureFormat.Depth;
GraphicsFormat stencilFormat = !isShadowMap && SystemInfo.IsFormatSupported(GraphicsFormat.R8_UInt, FormatUsage.StencilSampling) ? GraphicsFormat.R8_UInt : GraphicsFormat.None;
rt = new RenderTexture(width, height, (int)depthBufferBits, format, RenderTextureReadWrite.Linear)
{
hideFlags = HideFlags.HideAndDontSave,
volumeDepth = slices,
filterMode = filterMode,
wrapModeU = wrapModeU,
wrapModeV = wrapModeV,
wrapModeW = wrapModeW,
dimension = dimension,
enableRandomWrite = enableRandomWrite,
useMipMap = useMipMap,
autoGenerateMips = autoGenerateMips,
anisoLevel = anisoLevel,
mipMapBias = mipMapBias,
stencilFormat = stencilFormat,
antiAliasing = (int)msaaSamples,
bindTextureMS = bindTextureMS,
useDynamicScale = m_HardwareDynamicResRequested && useDynamicScale,
memorylessMode = memoryless,
vrUsage = vrUsage,
name = CoreUtils.GetRenderTargetAutoName(width, height, slices, format, name, mips: useMipMap, enableMSAA: enableMSAA, msaaSamples: msaaSamples)
};
}
else
{
rt = new RenderTexture(width, height, (int)depthBufferBits, colorFormat)
{
hideFlags = HideFlags.HideAndDontSave,
volumeDepth = slices,
filterMode = filterMode,
wrapModeU = wrapModeU,
wrapModeV = wrapModeV,
wrapModeW = wrapModeW,
dimension = dimension,
enableRandomWrite = enableRandomWrite,
useMipMap = useMipMap,
autoGenerateMips = autoGenerateMips,
anisoLevel = anisoLevel,
mipMapBias = mipMapBias,
antiAliasing = (int)msaaSamples,
bindTextureMS = bindTextureMS,
useDynamicScale = m_HardwareDynamicResRequested && useDynamicScale,
memorylessMode = memoryless,
vrUsage = vrUsage,
name = CoreUtils.GetRenderTargetAutoName(width, height, slices, colorFormat, dimension, name, mips: useMipMap, enableMSAA: enableMSAA, msaaSamples: msaaSamples, dynamicRes: useDynamicScale)
};
}
rt.Create();
var newRT = new RTHandle(this);
newRT.SetRenderTexture(rt);
newRT.useScaling = false;
newRT.m_EnableRandomWrite = enableRandomWrite;
newRT.m_EnableMSAA = enableMSAA;
newRT.m_EnableHWDynamicScale = useDynamicScale;
newRT.m_Name = name;
newRT.referenceSize = new Vector2Int(width, height);
return newRT;
}
// Next two methods are used to allocate RenderTexture that depend on the frame settings (resolution and msaa for now)
// RenderTextures allocated this way are meant to be defined by a scale of camera resolution (full/half/quarter resolution for example).
// The idea is that internally the system will scale up the size of all render texture so that it amortizes with time and not reallocate when a smaller size is required (which is what happens with TemporaryRTs).
// Since MSAA cannot be changed on the fly for a given RenderTexture, a separate instance will be created if the user requires it. This instance will be the one used after the next call of SetReferenceSize if MSAA is required.
/// <summary>
/// Allocate a new automatically sized RTHandle.
/// </summary>
/// <param name="scaleFactor">Constant scale for the RTHandle size computation.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns>A new RTHandle.</returns>
public RTHandle Alloc(
Vector2 scaleFactor,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0f,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
int width = Mathf.Max(Mathf.RoundToInt(scaleFactor.x * GetMaxWidth()), 1);
int height = Mathf.Max(Mathf.RoundToInt(scaleFactor.y * GetMaxHeight()), 1);
var rth = AllocAutoSizedRenderTexture(width,
height,
slices,
depthBufferBits,
colorFormat,
filterMode,
wrapMode,
dimension,
enableRandomWrite,
useMipMap,
autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
msaaSamples,
bindTextureMS,
useDynamicScale,
memoryless,
vrUsage,
name
);
rth.referenceSize = new Vector2Int(width, height);
rth.scaleFactor = scaleFactor;
return rth;
}
//
// You can provide your own scaling function for advanced scaling schemes (e.g. scaling to
// the next POT). The function takes a Vec2 as parameter that holds max width & height
// values for the current manager context and returns a Vec2 of the final size in pixels.
//
// var rth = Alloc(
// size => new Vector2Int(size.x / 2, size.y),
// [...]
// );
//
/// <summary>
/// Allocate a new automatically sized RTHandle.
/// </summary>
/// <param name="scaleFunc">Function used for the RTHandle size computation.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns>A new RTHandle.</returns>
public RTHandle Alloc(
ScaleFunc scaleFunc,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0f,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
var scaleFactor = scaleFunc(new Vector2Int(GetMaxWidth(), GetMaxHeight()));
int width = Mathf.Max(scaleFactor.x, 1);
int height = Mathf.Max(scaleFactor.y, 1);
var rth = AllocAutoSizedRenderTexture(width,
height,
slices,
depthBufferBits,
colorFormat,
filterMode,
wrapMode,
dimension,
enableRandomWrite,
useMipMap,
autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
msaaSamples,
bindTextureMS,
useDynamicScale,
memoryless,
vrUsage,
name
);
rth.referenceSize = new Vector2Int(width, height);
rth.scaleFunc = scaleFunc;
return rth;
}
// Internal function
RTHandle AllocAutoSizedRenderTexture(
int width,
int height,
int slices,
DepthBits depthBufferBits,
GraphicsFormat colorFormat,
FilterMode filterMode,
TextureWrapMode wrapMode,
TextureDimension dimension,
bool enableRandomWrite,
bool useMipMap,
bool autoGenerateMips,
bool isShadowMap,
int anisoLevel,
float mipMapBias,
MSAASamples msaaSamples,
bool bindTextureMS,
bool useDynamicScale,
RenderTextureMemoryless memoryless,
VRTextureUsage vrUsage,
string name
)
{
bool enableMSAA = msaaSamples != MSAASamples.None;
// Here user made a mistake in setting up msaa/bindMS, hence the warning
if (!enableMSAA && bindTextureMS == true)
{
Debug.LogWarning("RTHandle allocated without MSAA but with bindMS set to true, forcing bindMS to false.");
bindTextureMS = false;
}
// MSAA Does not support random read/write.
if (enableMSAA && (enableRandomWrite == true))
{
Debug.LogWarning("RTHandle that is MSAA-enabled cannot allocate MSAA RT with 'enableRandomWrite = true'.");
enableRandomWrite = false;
}
// We need to handle this in an explicit way since GraphicsFormat does not expose depth formats. TODO: Get rid of this branch once GraphicsFormat'll expose depth related formats
RenderTexture rt;
if (isShadowMap || depthBufferBits != DepthBits.None)
{
RenderTextureFormat format = isShadowMap ? RenderTextureFormat.Shadowmap : RenderTextureFormat.Depth;
GraphicsFormat stencilFormat = !isShadowMap && SystemInfo.IsFormatSupported(GraphicsFormat.R8_UInt, FormatUsage.StencilSampling) ? GraphicsFormat.R8_UInt : GraphicsFormat.None;
rt = new RenderTexture(width, height, (int)depthBufferBits, format, RenderTextureReadWrite.Linear)
{
hideFlags = HideFlags.HideAndDontSave,
volumeDepth = slices,
filterMode = filterMode,
wrapMode = wrapMode,
dimension = dimension,
enableRandomWrite = enableRandomWrite,
useMipMap = useMipMap,
autoGenerateMips = autoGenerateMips,
anisoLevel = anisoLevel,
mipMapBias = mipMapBias,
antiAliasing = (int)msaaSamples,
bindTextureMS = bindTextureMS,
useDynamicScale = m_HardwareDynamicResRequested && useDynamicScale,
memorylessMode = memoryless,
stencilFormat = stencilFormat,
vrUsage = vrUsage,
name = CoreUtils.GetRenderTargetAutoName(width, height, slices, colorFormat, dimension, name, mips: useMipMap, enableMSAA: enableMSAA, msaaSamples: msaaSamples, dynamicRes: useDynamicScale)
};
}
else
{
rt = new RenderTexture(width, height, (int)depthBufferBits, colorFormat)
{
hideFlags = HideFlags.HideAndDontSave,
volumeDepth = slices,
filterMode = filterMode,
wrapMode = wrapMode,
dimension = dimension,
enableRandomWrite = enableRandomWrite,
useMipMap = useMipMap,
autoGenerateMips = autoGenerateMips,
anisoLevel = anisoLevel,
mipMapBias = mipMapBias,
antiAliasing = (int)msaaSamples,
bindTextureMS = bindTextureMS,
useDynamicScale = m_HardwareDynamicResRequested && useDynamicScale,
memorylessMode = memoryless,
vrUsage = vrUsage,
name = CoreUtils.GetRenderTargetAutoName(width, height, slices, colorFormat, dimension, name, mips: useMipMap, enableMSAA: enableMSAA, msaaSamples: msaaSamples, dynamicRes: useDynamicScale)
};
}
rt.Create();
var rth = new RTHandle(this);
rth.SetRenderTexture(rt);
rth.m_EnableMSAA = enableMSAA;
rth.m_EnableRandomWrite = enableRandomWrite;
rth.useScaling = true;
rth.m_EnableHWDynamicScale = useDynamicScale;
rth.m_Name = name;
m_AutoSizedRTs.Add(rth);
return rth;
}
/// <summary>
/// Allocate a RTHandle from a regular RenderTexture.
/// </summary>
/// <param name="texture">Input texture</param>
/// <returns>A new RTHandle referencing the input texture.</returns>
public RTHandle Alloc(RenderTexture texture)
{
var rth = new RTHandle(this);
rth.SetRenderTexture(texture);
rth.m_EnableMSAA = false;
rth.m_EnableRandomWrite = false;
rth.useScaling = false;
rth.m_EnableHWDynamicScale = false;
rth.m_Name = texture.name;
return rth;
}
/// <summary>
/// Allocate a RTHandle from a regular Texture.
/// </summary>
/// <param name="texture">Input texture</param>
/// <returns>A new RTHandle referencing the input texture.</returns>
public RTHandle Alloc(Texture texture)
{
var rth = new RTHandle(this);
rth.SetTexture(texture);
rth.m_EnableMSAA = false;
rth.m_EnableRandomWrite = false;
rth.useScaling = false;
rth.m_EnableHWDynamicScale = false;
rth.m_Name = texture.name;
return rth;
}
/// <summary>
/// Allocate a RTHandle from a regular render target identifier.
/// </summary>
/// <param name="texture">Input render target identifier.</param>
/// <returns>A new RTHandle referencing the input render target identifier.</returns>
public RTHandle Alloc(RenderTargetIdentifier texture)
{
return Alloc(texture, "");
}
/// <summary>
/// Allocate a RTHandle from a regular render target identifier.
/// </summary>
/// <param name="texture">Input render target identifier.</param>
/// <param name="name">Name of the texture.</param>
/// <returns>A new RTHandle referencing the input render target identifier.</returns>
public RTHandle Alloc(RenderTargetIdentifier texture, string name)
{
var rth = new RTHandle(this);
rth.SetTexture(texture);
rth.m_EnableMSAA = false;
rth.m_EnableRandomWrite = false;
rth.useScaling = false;
rth.m_EnableHWDynamicScale = false;
rth.m_Name = name;
return rth;
}
private static RTHandle Alloc(RTHandle tex)
{
Debug.LogError("Allocation a RTHandle from another one is forbidden.");
return null;
}
internal string DumpRTInfo()
{
string result = "";
Array.Resize(ref m_AutoSizedRTsArray, m_AutoSizedRTs.Count);
m_AutoSizedRTs.CopyTo(m_AutoSizedRTsArray);
for (int i = 0, c = m_AutoSizedRTsArray.Length; i < c; ++i)
{
var rt = m_AutoSizedRTsArray[i].rt;
result = string.Format("{0}\nRT ({1})\t Format: {2} W: {3} H {4}\n", result, i, rt.format, rt.width, rt.height);
}
return result;
}
}
}

View File

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

View File

@@ -0,0 +1,554 @@
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// Default instance of a RTHandleSystem
/// </summary>
public static class RTHandles
{
static RTHandleSystem s_DefaultInstance = new RTHandleSystem();
/// <summary>
/// Maximum allocated width of the default RTHandle System
/// </summary>
public static int maxWidth { get { return s_DefaultInstance.GetMaxWidth(); } }
/// <summary>
/// Maximum allocated height of the default RTHandle System
/// </summary>
public static int maxHeight { get { return s_DefaultInstance.GetMaxHeight(); } }
/// <summary>
/// Current properties of the default RTHandle System
/// </summary>
public static RTHandleProperties rtHandleProperties { get { return s_DefaultInstance.rtHandleProperties; } }
/// <summary>
/// Allocate a new fixed sized RTHandle with the default RTHandle System.
/// </summary>
/// <param name="width">With of the RTHandle.</param>
/// <param name="height">Heigh of the RTHandle.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples for the RTHandle.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns></returns>
public static RTHandle Alloc(
int width,
int height,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
return s_DefaultInstance.Alloc(
width,
height,
slices,
depthBufferBits,
colorFormat,
filterMode,
wrapMode,
dimension,
enableRandomWrite,
useMipMap,
autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
msaaSamples,
bindTextureMS,
useDynamicScale,
memoryless,
vrUsage,
name
);
}
/// <summary>
/// Allocate a new fixed sized RTHandle with the default RTHandle System.
/// </summary>
/// <param name="width">With of the RTHandle.</param>
/// <param name="height">Heigh of the RTHandle.</param>
/// <param name="wrapModeU">U coordinate wrapping mode of the RTHandle.</param>
/// <param name="wrapModeV">V coordinate wrapping mode of the RTHandle.</param>
/// <param name="wrapModeW">W coordinate wrapping mode of the RTHandle.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples for the RTHandle.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns></returns>
public static RTHandle Alloc(
int width,
int height,
TextureWrapMode wrapModeU,
TextureWrapMode wrapModeV,
TextureWrapMode wrapModeW = TextureWrapMode.Repeat,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
return s_DefaultInstance.Alloc(
width,
height,
wrapModeU,
wrapModeV,
wrapModeW,
slices,
depthBufferBits,
colorFormat,
filterMode,
dimension,
enableRandomWrite,
useMipMap,
autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
msaaSamples,
bindTextureMS,
useDynamicScale,
memoryless,
vrUsage,
name
);
}
/// <summary>
/// Allocate a new fixed sized RTHandle with the default RTHandle System.
/// </summary>
/// <param name="descriptor">RenderTexture descriptor of the RTHandle.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns>A new RTHandle.</returns>
public static RTHandle Alloc(
in RenderTextureDescriptor descriptor,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
string name = ""
)
{
var result = s_DefaultInstance.Alloc(
descriptor.width,
descriptor.height,
descriptor.volumeDepth,
(DepthBits)descriptor.depthBufferBits,
descriptor.graphicsFormat,
filterMode,
wrapMode,
descriptor.dimension,
descriptor.enableRandomWrite,
descriptor.useMipMap,
descriptor.autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
(MSAASamples)descriptor.msaaSamples,
descriptor.bindMS,
descriptor.useDynamicScale,
descriptor.memoryless,
descriptor.vrUsage,
name
);
return result;
}
/// <summary>
/// Allocate a new automatically sized RTHandle for the default RTHandle System.
/// </summary>
/// <param name="scaleFactor">Constant scale for the RTHandle size computation.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns>A new RTHandle.</returns>
public static RTHandle Alloc(
Vector2 scaleFactor,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
return s_DefaultInstance.Alloc(
scaleFactor,
slices,
depthBufferBits,
colorFormat,
filterMode,
wrapMode,
dimension,
enableRandomWrite,
useMipMap,
autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
msaaSamples,
bindTextureMS,
useDynamicScale,
memoryless,
vrUsage,
name
);
}
/// <summary>
/// Allocate a new automatically sized RTHandle for the default RTHandle System.
/// </summary>
/// <param name="scaleFactor">Constant scale for the RTHandle size computation.</param>
/// <param name="descriptor">RenderTexture descriptor of the RTHandle.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns>A new RTHandle.</returns>
public static RTHandle Alloc(
Vector2 scaleFactor,
in RenderTextureDescriptor descriptor,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
string name = ""
)
{
return s_DefaultInstance.Alloc(
scaleFactor,
descriptor.volumeDepth,
(DepthBits)descriptor.depthBufferBits,
descriptor.graphicsFormat,
filterMode,
wrapMode,
descriptor.dimension,
descriptor.enableRandomWrite,
descriptor.useMipMap,
descriptor.autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
(MSAASamples)descriptor.msaaSamples,
descriptor.bindMS,
descriptor.useDynamicScale,
descriptor.memoryless,
descriptor.vrUsage,
name
);
}
/// <summary>
/// Allocate a new automatically sized RTHandle for the default RTHandle System.
/// </summary>
/// <param name="scaleFunc">Function used for the RTHandle size computation.</param>
/// <param name="slices">Number of slices of the RTHandle.</param>
/// <param name="depthBufferBits">Bit depths of a depth buffer.</param>
/// <param name="colorFormat">GraphicsFormat of a color buffer.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="dimension">Texture dimension of the RTHandle.</param>
/// <param name="enableRandomWrite">Set to true to enable UAV random read writes on the texture.</param>
/// <param name="useMipMap">Set to true if the texture should have mipmaps.</param>
/// <param name="autoGenerateMips">Set to true to automatically generate mipmaps.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="msaaSamples">Number of MSAA samples.</param>
/// <param name="bindTextureMS">Set to true if the texture needs to be bound as a multisampled texture in the shader.</param>
/// <param name="useDynamicScale">Set to true to use hardware dynamic scaling.</param>
/// <param name="memoryless">Use this property to set the render texture memoryless modes.</param>
/// <param name="vrUsage">Special treatment of the VR eye texture used in stereoscopic rendering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns></returns>
public static RTHandle Alloc(
ScaleFunc scaleFunc,
int slices = 1,
DepthBits depthBufferBits = DepthBits.None,
GraphicsFormat colorFormat = GraphicsFormat.R8G8B8A8_SRGB,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
TextureDimension dimension = TextureDimension.Tex2D,
bool enableRandomWrite = false,
bool useMipMap = false,
bool autoGenerateMips = true,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
MSAASamples msaaSamples = MSAASamples.None,
bool bindTextureMS = false,
bool useDynamicScale = false,
RenderTextureMemoryless memoryless = RenderTextureMemoryless.None,
VRTextureUsage vrUsage = VRTextureUsage.None,
string name = ""
)
{
return s_DefaultInstance.Alloc(
scaleFunc,
slices,
depthBufferBits,
colorFormat,
filterMode,
wrapMode,
dimension,
enableRandomWrite,
useMipMap,
autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
msaaSamples,
bindTextureMS,
useDynamicScale,
memoryless,
vrUsage,
name
);
}
/// <summary>
/// Allocate a new automatically sized RTHandle for the default RTHandle System.
/// </summary>
/// <param name="scaleFunc">Function used for the RTHandle size computation.</param>
/// <param name="descriptor">RenderTexture descriptor of the RTHandle.</param>
/// <param name="filterMode">Filtering mode of the RTHandle.</param>
/// <param name="wrapMode">Addressing mode of the RTHandle.</param>
/// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
/// <param name="anisoLevel">Anisotropic filtering level.</param>
/// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
/// <param name="name">Name of the RTHandle.</param>
/// <returns>A new RTHandle.</returns>
public static RTHandle Alloc(
ScaleFunc scaleFunc,
in RenderTextureDescriptor descriptor,
FilterMode filterMode = FilterMode.Point,
TextureWrapMode wrapMode = TextureWrapMode.Repeat,
bool isShadowMap = false,
int anisoLevel = 1,
float mipMapBias = 0,
string name = ""
)
{
return s_DefaultInstance.Alloc(
scaleFunc,
descriptor.volumeDepth,
(DepthBits)descriptor.depthBufferBits,
descriptor.graphicsFormat,
filterMode,
wrapMode,
descriptor.dimension,
descriptor.enableRandomWrite,
descriptor.useMipMap,
descriptor.autoGenerateMips,
isShadowMap,
anisoLevel,
mipMapBias,
(MSAASamples)descriptor.msaaSamples,
descriptor.bindMS,
descriptor.useDynamicScale,
descriptor.memoryless,
descriptor.vrUsage,
name
);
}
/// <summary>
/// Allocate a RTHandle from a regular Texture for the default RTHandle system.
/// </summary>
/// <param name="tex">Input texture</param>
/// <returns>A new RTHandle referencing the input texture.</returns>
public static RTHandle Alloc(Texture tex)
{
return s_DefaultInstance.Alloc(tex);
}
/// <summary>
/// Allocate a RTHandle from a regular RenderTexture for the default RTHandle system.
/// </summary>
/// <param name="tex">Input texture</param>
/// <returns>A new RTHandle referencing the input texture.</returns>
public static RTHandle Alloc(RenderTexture tex)
{
return s_DefaultInstance.Alloc(tex);
}
/// <summary>
/// Allocate a RTHandle from a regular render target identifier for the default RTHandle system.
/// </summary>
/// <param name="tex">Input render target identifier.</param>
/// <returns>A new RTHandle referencing the input render target identifier.</returns>
public static RTHandle Alloc(RenderTargetIdentifier tex)
{
return s_DefaultInstance.Alloc(tex);
}
/// <summary>
/// Allocate a RTHandle from a regular render target identifier for the default RTHandle system.
/// </summary>
/// <param name="tex">Input render target identifier.</param>
/// <param name="name">Name of the render target.</param>
/// <returns>A new RTHandle referencing the input render target identifier.</returns>
public static RTHandle Alloc(RenderTargetIdentifier tex, string name)
{
return s_DefaultInstance.Alloc(tex, name);
}
private static RTHandle Alloc(RTHandle tex)
{
Debug.LogError("Allocation a RTHandle from another one is forbidden.");
return null;
}
/// <summary>
/// Initialize the default RTHandle system.
/// </summary>
/// <param name="width">Initial reference rendering width.</param>
/// <param name="height">Initial reference rendering height.</param>
/// <param name="useLegacyDynamicResControl">Use legacy hardware DynamicResolution control in the default RTHandle system.</param>
public static void Initialize(int width, int height, bool useLegacyDynamicResControl = false)
{
s_DefaultInstance.Initialize(width, height, useLegacyDynamicResControl);
}
/// <summary>
/// Release memory of a RTHandle from the default RTHandle System
/// </summary>
/// <param name="rth">RTHandle that should be released.</param>
public static void Release(RTHandle rth)
{
s_DefaultInstance.Release(rth);
}
/// <summary>
/// Enable or disable hardware dynamic resolution for the default RTHandle System
/// </summary>
/// <param name="hwDynamicResRequested">State of hardware dynamic resolution.</param>
public static void SetHardwareDynamicResolutionState(bool hwDynamicResRequested)
{
s_DefaultInstance.SetHardwareDynamicResolutionState(hwDynamicResRequested);
}
/// <summary>
/// Sets the reference rendering size for subsequent rendering for the default RTHandle System
/// </summary>
/// <param name="width">Reference rendering width for subsequent rendering.</param>
/// <param name="height">Reference rendering height for subsequent rendering.</param>
public static void SetReferenceSize(int width, int height)
{
s_DefaultInstance.SetReferenceSize(width, height);
}
/// <summary>
/// Reset the reference size of the system and reallocate all textures.
/// </summary>
/// <param name="width">New width.</param>
/// <param name="height">New height.</param>
public static void ResetReferenceSize(int width, int height)
{
s_DefaultInstance.ResetReferenceSize(width, height);
}
/// <summary>
/// Returns the ratio against the current target's max resolution
/// </summary>
/// <param name="width">width to utilize</param>
/// <param name="height">height to utilize</param>
/// <returns> retruns the width,height / maxTargetSize.xy ratio. </returns>
public static Vector2 CalculateRatioAgainstMaxSize(int width, int height)
{
return s_DefaultInstance.CalculateRatioAgainstMaxSize(new Vector2Int(width, height));
}
}
}

View File

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

View File

@@ -0,0 +1,772 @@
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using System;
namespace UnityEngine.Rendering
{
class AtlasAllocator
{
private class AtlasNode
{
public AtlasNode m_RightChild = null;
public AtlasNode m_BottomChild = null;
public Vector4 m_Rect = new Vector4(0, 0, 0, 0); // x,y is width and height (scale) z,w offset into atlas (offset)
public AtlasNode Allocate(ref ObjectPool<AtlasNode> pool, int width, int height, bool powerOfTwoPadding)
{
// not a leaf node, try children
if (m_RightChild != null)
{
AtlasNode node = m_RightChild.Allocate(ref pool, width, height, powerOfTwoPadding);
if (node == null)
{
node = m_BottomChild.Allocate(ref pool, width, height, powerOfTwoPadding);
}
return node;
}
int wPadd = 0;
int hPadd = 0;
if (powerOfTwoPadding)
{
wPadd = (int)m_Rect.x % width;
hPadd = (int)m_Rect.y % height;
}
//leaf node, check for fit
if ((width <= m_Rect.x - wPadd) && (height <= m_Rect.y - hPadd))
{
// perform the split
m_RightChild = pool.Get();
m_BottomChild = pool.Get();
m_Rect.z += wPadd;
m_Rect.w += hPadd;
m_Rect.x -= wPadd;
m_Rect.y -= hPadd;
if (width > height) // logic to decide which way to split
{
// +--------+------+
m_RightChild.m_Rect.z = m_Rect.z + width; // | | |
m_RightChild.m_Rect.w = m_Rect.w; // +--------+------+
m_RightChild.m_Rect.x = m_Rect.x - width; // | |
m_RightChild.m_Rect.y = height; // | |
// +---------------+
m_BottomChild.m_Rect.z = m_Rect.z;
m_BottomChild.m_Rect.w = m_Rect.w + height;
m_BottomChild.m_Rect.x = m_Rect.x;
m_BottomChild.m_Rect.y = m_Rect.y - height;
}
else
{ // +---+-----------+
m_RightChild.m_Rect.z = m_Rect.z + width; // | | |
m_RightChild.m_Rect.w = m_Rect.w; // | | |
m_RightChild.m_Rect.x = m_Rect.x - width; // +---+ +
m_RightChild.m_Rect.y = m_Rect.y; // | | |
// +---+-----------+
m_BottomChild.m_Rect.z = m_Rect.z;
m_BottomChild.m_Rect.w = m_Rect.w + height;
m_BottomChild.m_Rect.x = width;
m_BottomChild.m_Rect.y = m_Rect.y - height;
}
m_Rect.x = width;
m_Rect.y = height;
return this;
}
return null;
}
public void Release(ref ObjectPool<AtlasNode> pool)
{
if (m_RightChild != null)
{
m_RightChild.Release(ref pool);
m_BottomChild.Release(ref pool);
pool.Release(m_RightChild);
pool.Release(m_BottomChild);
}
m_RightChild = null;
m_BottomChild = null;
m_Rect = Vector4.zero;
}
}
private AtlasNode m_Root;
private int m_Width;
private int m_Height;
private bool powerOfTwoPadding;
private ObjectPool<AtlasNode> m_NodePool;
public AtlasAllocator(int width, int height, bool potPadding)
{
m_Root = new AtlasNode();
m_Root.m_Rect.Set(width, height, 0, 0);
m_Width = width;
m_Height = height;
powerOfTwoPadding = potPadding;
m_NodePool = new ObjectPool<AtlasNode>(_ => { }, _ => { });
}
public bool Allocate(ref Vector4 result, int width, int height)
{
AtlasNode node = m_Root.Allocate(ref m_NodePool, width, height, powerOfTwoPadding);
if (node != null)
{
result = node.m_Rect;
return true;
}
else
{
result = Vector4.zero;
return false;
}
}
public void Reset()
{
m_Root.Release(ref m_NodePool);
m_Root.m_Rect.Set(m_Width, m_Height, 0, 0);
}
}
/// <summary>
/// A generic Atlas texture of 2D textures.
/// An atlas texture is a texture collection that collects multiple sub-textures into a single big texture.
/// Sub-texture allocation for Texture2DAtlas is static and will not change after initial allocation.
/// Does not add mipmap padding for sub-textures.
/// </summary>
public class Texture2DAtlas
{
private enum BlitType
{
Default,
CubeTo2DOctahedral,
SingleChannel,
CubeTo2DOctahedralSingleChannel,
}
/// <summary>
/// Texture is not on the GPU or is not up to date.
/// </summary>
private protected const int kGPUTexInvalid = 0;
/// <summary>
/// Texture Mip0 is on the GPU and up to date.
/// </summary>
private protected const int kGPUTexValidMip0 = 1;
/// <summary>
/// Texture and all mips are on the GPU and up to date.
/// </summary>
private protected const int kGPUTexValidMipAll = 2;
/// <summary>
/// The texture for the atlas.
/// </summary>
private protected RTHandle m_AtlasTexture = null;
/// <summary>
/// Width of the atlas.
/// </summary>
private protected int m_Width;
/// <summary>
/// Height of the atlas.
/// </summary>
private protected int m_Height;
/// <summary>
/// Format of the atlas.
/// </summary>
private protected GraphicsFormat m_Format;
/// <summary>
/// Atlas uses mip maps.
/// </summary>
private protected bool m_UseMipMaps;
bool m_IsAtlasTextureOwner = false;
private AtlasAllocator m_AtlasAllocator = null;
private Dictionary<int, (Vector4 scaleOffset, Vector2Int size)> m_AllocationCache = new Dictionary<int, (Vector4, Vector2Int)>();
private Dictionary<int, int> m_IsGPUTextureUpToDate = new Dictionary<int, int>();
private Dictionary<int, int> m_TextureHashes = new Dictionary<int, int>();
static readonly Vector4 fullScaleOffset = new Vector4(1, 1, 0, 0);
// Maximum mip padding that can be applied to the textures in the atlas (1 << 10 = 1024 pixels)
static readonly int s_MaxMipLevelPadding = 10;
/// <summary>
/// Maximum mip padding (pow2) that can be applied to the textures in the atlas
/// </summary>
public static int maxMipLevelPadding => s_MaxMipLevelPadding;
/// <summary>
/// Handle to the texture of the atlas.
/// </summary>
public RTHandle AtlasTexture
{
get
{
return m_AtlasTexture;
}
}
/// <summary>
/// Creates a new empty texture atlas.
/// </summary>
/// <param name="width">Width of the atlas in pixels.</param>
/// <param name="height">Height of atlas in pixels.</param>
/// <param name="format">GraphicsFormat of the atlas.</param>
/// <param name="filterMode">Filtering mode of the atlas.</param>
/// <param name="powerOfTwoPadding">Power of two padding.</param>
/// <param name="name">Name of the atlas</param>
/// <param name="useMipMap">Use mip maps</param>
public Texture2DAtlas(int width, int height, GraphicsFormat format, FilterMode filterMode = FilterMode.Point, bool powerOfTwoPadding = false, string name = "", bool useMipMap = true)
{
m_Width = width;
m_Height = height;
m_Format = format;
m_UseMipMaps = useMipMap;
m_AtlasTexture = RTHandles.Alloc(
width: m_Width,
height: m_Height,
filterMode: filterMode,
colorFormat: m_Format,
wrapMode: TextureWrapMode.Clamp,
useMipMap: useMipMap,
autoGenerateMips: false,
name: name
);
m_IsAtlasTextureOwner = true;
// We clear on create to avoid garbage data to be present in the atlas
int mipCount = useMipMap ? GetTextureMipmapCount(m_Width, m_Height) : 1;
for (int mipIdx = 0; mipIdx < mipCount; ++mipIdx)
{
Graphics.SetRenderTarget(m_AtlasTexture, mipIdx);
GL.Clear(false, true, Color.clear);
}
m_AtlasAllocator = new AtlasAllocator(width, height, powerOfTwoPadding);
}
/// <summary>
/// Release atlas resources.
/// </summary>
public void Release()
{
ResetAllocator();
if (m_IsAtlasTextureOwner) { RTHandles.Release(m_AtlasTexture); }
}
/// <summary>
/// Clear atlas sub-texture allocations.
/// </summary>
public void ResetAllocator()
{
m_AtlasAllocator.Reset();
m_AllocationCache.Clear();
m_IsGPUTextureUpToDate.Clear(); // mark all GPU textures as invalid.
}
/// <summary>
/// Clear atlas texture.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
public void ClearTarget(CommandBuffer cmd)
{
int mipCount = (m_UseMipMaps) ? GetTextureMipmapCount(m_Width, m_Height) : 1;
// clear the atlas by blitting a black texture at every mips
for (int mipLevel = 0; mipLevel < mipCount; mipLevel++)
{
cmd.SetRenderTarget(m_AtlasTexture, mipLevel);
Blitter.BlitQuad(cmd, Texture2D.blackTexture, fullScaleOffset, fullScaleOffset, mipLevel, true);
}
m_IsGPUTextureUpToDate.Clear(); // mark all GPU textures as invalid.
}
/// <summary>
/// Return texture mip map count based on the width and height.
/// </summary>
/// <param name="width">The texture width in pixels.</param>
/// <param name="height">The texture height in pixels.</param>
/// <returns>The number of mip maps.</returns>
private protected int GetTextureMipmapCount(int width, int height)
{
if (!m_UseMipMaps)
return 1;
// We don't care about the real mipmap count in the texture because they are generated by the atlas
float maxSize = Mathf.Max(width, height);
return Mathf.FloorToInt(Mathf.Log(maxSize, 2)) + 1;
}
/// <summary>
/// Test if a texture is a 2D texture.
/// </summary>
/// <param name="texture">Source texture.</param>
/// <returns>True if texture is 2D, false otherwise.</returns>
private protected bool Is2D(Texture texture)
{
RenderTexture rt = texture as RenderTexture;
return (texture is Texture2D || rt?.dimension == TextureDimension.Tex2D);
}
/// <summary>
/// Checks if single/multi/single channel format conversion is required.
/// </summary>
/// <param name="source">Blit source texture</param>
/// <param name="destination">Blit destination texture</param>
/// <returns>true on single channel conversion false otherwise</returns>
private protected bool IsSingleChannelBlit(Texture source, Texture destination)
{
var srcCount = GraphicsFormatUtility.GetComponentCount(source.graphicsFormat);
var dstCount = GraphicsFormatUtility.GetComponentCount(destination.graphicsFormat);
if (srcCount == 1 || dstCount == 1)
{
// One to many, many to one
if (srcCount != dstCount)
return true;
// Single channel swizzle
var srcSwizzle =
((1 << ((int)GraphicsFormatUtility.GetSwizzleA(source.graphicsFormat) & 0x7)) << 24) |
((1 << ((int)GraphicsFormatUtility.GetSwizzleB(source.graphicsFormat) & 0x7)) << 16) |
((1 << ((int)GraphicsFormatUtility.GetSwizzleG(source.graphicsFormat) & 0x7)) << 8) |
((1 << ((int)GraphicsFormatUtility.GetSwizzleR(source.graphicsFormat) & 0x7)));
var dstSwizzle =
((1 << ((int)GraphicsFormatUtility.GetSwizzleA(destination.graphicsFormat) & 0x7)) << 24) |
((1 << ((int)GraphicsFormatUtility.GetSwizzleB(destination.graphicsFormat) & 0x7)) << 16) |
((1 << ((int)GraphicsFormatUtility.GetSwizzleG(destination.graphicsFormat) & 0x7)) << 8) |
((1 << ((int)GraphicsFormatUtility.GetSwizzleR(destination.graphicsFormat) & 0x7)));
if (srcSwizzle != dstSwizzle)
return true;
}
return false;
}
private void Blit2DTexture(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips, BlitType blitType)
{
int mipCount = GetTextureMipmapCount(texture.width, texture.height);
if (!blitMips)
mipCount = 1;
for (int mipLevel = 0; mipLevel < mipCount; mipLevel++)
{
cmd.SetRenderTarget(m_AtlasTexture, mipLevel);
switch (blitType)
{
case BlitType.Default: Blitter.BlitQuad(cmd, texture, sourceScaleOffset, scaleOffset, mipLevel, true); break;
case BlitType.CubeTo2DOctahedral: Blitter.BlitCubeToOctahedral2DQuad(cmd, texture, scaleOffset, mipLevel); break;
case BlitType.SingleChannel: Blitter.BlitQuadSingleChannel(cmd, texture, sourceScaleOffset, scaleOffset, mipLevel); break;
case BlitType.CubeTo2DOctahedralSingleChannel: Blitter.BlitCubeToOctahedral2DQuadSingleChannel(cmd, texture, scaleOffset, mipLevel); break;
}
}
}
/// <summary>
/// Mark texture valid on the GPU.
/// </summary>
/// <param name="instanceId">Texture instance ID.</param>
/// <param name="mipAreValid">Texture has valid mip maps.</param>
private protected void MarkGPUTextureValid(int instanceId, bool mipAreValid = false)
{
m_IsGPUTextureUpToDate[instanceId] = (mipAreValid) ? kGPUTexValidMipAll : kGPUTexValidMip0;
}
/// <summary>
/// Mark texture invalid on the GPU.
/// </summary>
/// <param name="instanceId">Texture instance ID.</param>
private protected void MarkGPUTextureInvalid(int instanceId) => m_IsGPUTextureUpToDate[instanceId] = kGPUTexInvalid;
/// <summary>
/// Blit 2D texture into the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public virtual void BlitTexture(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips = true, int overrideInstanceID = -1)
{
// This atlas only support 2D texture so we only blit 2D textures
if (Is2D(texture))
{
BlitType blitType = BlitType.Default;
if (IsSingleChannelBlit(texture, m_AtlasTexture.m_RT))
blitType = BlitType.SingleChannel;
Blit2DTexture(cmd, scaleOffset, texture, sourceScaleOffset, blitMips, blitType);
var instanceID = overrideInstanceID != -1 ? overrideInstanceID : GetTextureID(texture);
MarkGPUTextureValid(instanceID, blitMips);
m_TextureHashes[instanceID] = CoreUtils.GetTextureHash(texture);
}
}
/// <summary>
/// Blit octahedral texture into the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public virtual void BlitOctahedralTexture(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, Vector4 sourceScaleOffset, bool blitMips = true, int overrideInstanceID = -1)
{
// Default implementation. No padding in Texture2DAtlas, no need to handle specially.
BlitTexture(cmd, scaleOffset, texture, sourceScaleOffset, blitMips, overrideInstanceID);
}
/// <summary>
/// Blit and project Cube texture into a 2D texture in the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
public virtual void BlitCubeTexture2D(CommandBuffer cmd, Vector4 scaleOffset, Texture texture, bool blitMips = true, int overrideInstanceID = -1)
{
Debug.Assert(texture.dimension == TextureDimension.Cube);
// This atlas only support 2D texture so we map Cube into set of 2D textures
if (texture.dimension == TextureDimension.Cube)
{
BlitType blitType = BlitType.CubeTo2DOctahedral;
if (IsSingleChannelBlit(texture, m_AtlasTexture.m_RT))
blitType = BlitType.CubeTo2DOctahedralSingleChannel;
// By default blit cube into a single octahedral 2D texture quad
Blit2DTexture(cmd, scaleOffset, texture, new Vector4(1.0f, 1.0f, 0.0f, 0.0f), blitMips, blitType);
var instanceID = overrideInstanceID != -1 ? overrideInstanceID : GetTextureID(texture);
MarkGPUTextureValid(instanceID, blitMips);
m_TextureHashes[instanceID] = CoreUtils.GetTextureHash(texture);
}
}
/// <summary>
/// Allocate space from the atlas for a texture and copy texture contents into the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="texture">Source Texture</param>
/// <param name="width">Request width in pixels.</param>
/// <param name="height">Request height in pixels.</param>
/// <param name="overrideInstanceID">Override texture instance ID.</param>
/// <returns></returns>
public virtual bool AllocateTexture(CommandBuffer cmd, ref Vector4 scaleOffset, Texture texture, int width, int height, int overrideInstanceID = -1)
{
var instanceID = overrideInstanceID != -1 ? overrideInstanceID : GetTextureID(texture);
bool allocated = AllocateTextureWithoutBlit(instanceID, width, height, ref scaleOffset);
if (allocated)
{
if (Is2D(texture))
BlitTexture(cmd, scaleOffset, texture, fullScaleOffset);
else
BlitCubeTexture2D(cmd, scaleOffset, texture, true);
// texture is up to date
MarkGPUTextureValid(instanceID, true);
m_TextureHashes[instanceID] = CoreUtils.GetTextureHash(texture);
}
return allocated;
}
/// <summary>
/// Allocate space from the atlas for a texture.
/// </summary>
/// <param name="texture">Source texture.</param>
/// <param name="width">Request width in pixels.</param>
/// <param name="height">Request height in pixels.</param>
/// <param name="scaleOffset">Allocated scale (.xy) and offset (.zw).</param>
/// <returns>True on success, false otherwise.</returns>
public bool AllocateTextureWithoutBlit(Texture texture, int width, int height, ref Vector4 scaleOffset)
=> AllocateTextureWithoutBlit(texture.GetInstanceID(), width, height, ref scaleOffset);
/// <summary>
/// Allocate space from the atlas for a texture.
/// </summary>
/// <param name="instanceId">Source texture instance ID.</param>
/// <param name="width">Request width in pixels.</param>
/// <param name="height">Request height in pixels.</param>
/// <param name="scaleOffset">Allocated scale (.xy) and offset (.zw).</param>
/// <returns>True on success, false otherwise.</returns>
public virtual bool AllocateTextureWithoutBlit(int instanceId, int width, int height, ref Vector4 scaleOffset)
{
scaleOffset = Vector4.zero;
if (m_AtlasAllocator.Allocate(ref scaleOffset, width, height))
{
scaleOffset.Scale(new Vector4(1.0f / m_Width, 1.0f / m_Height, 1.0f / m_Width, 1.0f / m_Height));
m_AllocationCache[instanceId] = (scaleOffset, new Vector2Int(width, height));
MarkGPUTextureInvalid(instanceId); // the texture data haven't been uploaded
m_TextureHashes[instanceId] = -1;
return true;
}
else
{
return false;
}
}
/// <summary>
/// Compute hash from texture properties.
/// </summary>
/// <param name="textureA">Source texture A.</param>
/// <param name="textureB">Source texture B.</param>
/// <returns>Hash of texture porperties.</returns>
private protected int GetTextureHash(Texture textureA, Texture textureB)
{
int hash = CoreUtils.GetTextureHash(textureA) + 23 * CoreUtils.GetTextureHash(textureB);
return hash;
}
/// <summary>
/// Get sub-texture ID for the atlas.
/// </summary>
/// <param name="texture">Source texture.</param>
/// <returns>Texture instance ID.</returns>
public int GetTextureID(Texture texture)
{
return texture.GetInstanceID();
}
/// <summary>
/// Get sub-texture ID for the atlas.
/// </summary>
/// <param name="textureA">Source texture A.</param>
/// <param name="textureB">Source texture B.</param>
/// <returns>Combined texture instance ID.</returns>
public int GetTextureID(Texture textureA, Texture textureB)
{
return GetTextureID(textureA) + 23 * GetTextureID(textureB);
}
/// <summary>
/// Check if the atlas contains the textures.
/// </summary>
/// <param name="scaleOffset">Texture scale (.xy) and offset (.zw).</param>
/// <param name="textureA">Source texture A.</param>
/// <param name="textureB">Source texture B.</param>
/// <returns>True if the texture is in the atlas, false otherwise.</returns>
public bool IsCached(out Vector4 scaleOffset, Texture textureA, Texture textureB)
=> IsCached(out scaleOffset, GetTextureID(textureA, textureB));
/// <summary>
/// Check if the atlas contains the textures.
/// </summary>
/// <param name="scaleOffset">Texture scale (.xy) and offset (.zw).</param>
/// <param name="texture">Source texture</param>
/// <returns>True if the texture is in the atlas, false otherwise.</returns>
public bool IsCached(out Vector4 scaleOffset, Texture texture)
=> IsCached(out scaleOffset, GetTextureID(texture));
/// <summary>
/// Check if the atlas contains the texture.
/// </summary>
/// <param name="scaleOffset">Texture scale (.xy) and offset (.zw).</param>
/// <param name="id">Source texture instance ID.</param>
/// <returns></returns>
public bool IsCached(out Vector4 scaleOffset, int id)
{
bool cached = m_AllocationCache.TryGetValue(id, out var value);
scaleOffset = value.scaleOffset;
return cached;
}
/// <summary>
/// Get cached texture size.
/// </summary>
/// <param name="id">Source texture instance ID.</param>
/// <returns>Texture size.</returns>
internal Vector2Int GetCachedTextureSize(int id)
{
m_AllocationCache.TryGetValue(id, out var value);
return value.size;
}
/// <summary>
/// Check if contents of a texture needs to be updated in the atlas.
/// </summary>
/// <param name="texture">Source texture.</param>
/// <param name="needMips">Texture uses mips.</param>
/// <returns>True if texture needs update, false otherwise.</returns>
public virtual bool NeedsUpdate(Texture texture, bool needMips = false)
{
RenderTexture rt = texture as RenderTexture;
int key = GetTextureID(texture);
int textureHash = CoreUtils.GetTextureHash(texture);
// Update the render texture if needed
if (rt != null)
{
int updateCount;
if (m_IsGPUTextureUpToDate.TryGetValue(key, out updateCount))
{
if (rt.updateCount != updateCount)
{
m_IsGPUTextureUpToDate[key] = (int)rt.updateCount;
return true;
}
}
else
{
m_IsGPUTextureUpToDate[key] = (int)rt.updateCount;
}
}
// In case the texture settings/import settings have changed, we need to update it
else if (m_TextureHashes.TryGetValue(key, out int hash) && hash != textureHash)
{
m_TextureHashes[key] = textureHash;
return true;
}
// For regular textures, values == 0 means that their GPU data needs to be updated (either because
// the atlas have been re-layouted or the texture have never been uploaded. We also check if the mips
// are valid for the texture if we need them
else if (m_IsGPUTextureUpToDate.TryGetValue(key, out var value))
return value == kGPUTexInvalid || (needMips && value == kGPUTexValidMip0);
return false;
}
/// <summary>
/// Check if contents of a texture needs to be updated in the atlas.
/// </summary>
/// <param name="textureA">Source texture A.</param>
/// <param name="textureB">Source texture B.</param>
/// <param name="needMips">Texture uses mips.</param>
/// <returns>True if texture needs update, false otherwise.</returns>
public virtual bool NeedsUpdate(Texture textureA, Texture textureB, bool needMips = false)
{
RenderTexture rtA = textureA as RenderTexture;
RenderTexture rtB = textureB as RenderTexture;
int key = GetTextureID(textureA, textureB);
int textureHash = GetTextureHash(textureA, textureB);
// Update the render texture if needed
if (rtA != null || rtB != null)
{
int updateCount;
if (m_IsGPUTextureUpToDate.TryGetValue(key, out updateCount))
{
if (rtA != null && rtB != null && Math.Min(rtA.updateCount, rtB.updateCount) != updateCount)
{
m_IsGPUTextureUpToDate[key] = (int)Math.Min(rtA.updateCount, rtB.updateCount);
return true;
}
else if (rtA != null && rtA.updateCount != updateCount)
{
m_IsGPUTextureUpToDate[key] = (int)rtA.updateCount;
return true;
}
else if (rtB != null && rtB.updateCount != updateCount)
{
m_IsGPUTextureUpToDate[key] = (int)rtB.updateCount;
return true;
}
}
else
{
m_IsGPUTextureUpToDate[key] = textureHash;
}
}
// In case the texture settings/import settings have changed, we need to update it
else if (m_TextureHashes.TryGetValue(key, out int hash) && hash != textureHash)
{
m_TextureHashes[key] = key;
return true;
}
// For regular textures, values == 0 means that their GPU data needs to be updated (either because
// the atlas have been re-layouted or the texture have never been uploaded. We also check if the mips
// are valid for the texture if we need them
else if (m_IsGPUTextureUpToDate.TryGetValue(key, out var value))
return value == kGPUTexInvalid || (needMips && value == kGPUTexValidMip0);
return false;
}
/// <summary>
/// Add a texture into the atlas.
/// </summary>
/// <param name="cmd">Command buffer used for texture copy.</param>
/// <param name="scaleOffset">Sub-texture rectangle for the added texture. Scale in .xy, offset int .zw</param>
/// <param name="texture">The texture to be added.</param>
/// <returns>True if the atlas contains the texture, false otherwise.</returns>
public virtual bool AddTexture(CommandBuffer cmd, ref Vector4 scaleOffset, Texture texture)
{
if (IsCached(out scaleOffset, texture))
return true;
return AllocateTexture(cmd, ref scaleOffset, texture, texture.width, texture.height);
}
/// <summary>
/// Update a texture in the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="oldTexture">Texture in atlas.</param>
/// <param name="newTexture">Replacement source texture.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="sourceScaleOffset">Source scale (.xy) and offset(.zw).</param>
/// <param name="updateIfNeeded">Enable texture blit.</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <returns>True on success, false otherwise.</returns>
public virtual bool UpdateTexture(CommandBuffer cmd, Texture oldTexture, Texture newTexture, ref Vector4 scaleOffset, Vector4 sourceScaleOffset, bool updateIfNeeded = true, bool blitMips = true)
{
// In case the old texture is here, we Blit the new one at the scale offset of the old one
if (IsCached(out scaleOffset, oldTexture))
{
if (updateIfNeeded && NeedsUpdate(newTexture))
{
if (Is2D(newTexture))
BlitTexture(cmd, scaleOffset, newTexture, sourceScaleOffset, blitMips);
else
BlitCubeTexture2D(cmd, scaleOffset, newTexture, blitMips);
MarkGPUTextureValid(GetTextureID(newTexture), blitMips); // texture is up to date
}
return true;
}
else // else we try to allocate the updated texture
{
return AllocateTexture(cmd, ref scaleOffset, newTexture, newTexture.width, newTexture.height);
}
}
/// <summary>
/// Update a texture in the atlas.
/// </summary>
/// <param name="cmd">Target command buffer for graphics commands.</param>
/// <param name="texture">Texture in atlas.</param>
/// <param name="scaleOffset">Destination scale (.xy) and offset (.zw)</param>
/// <param name="updateIfNeeded">Enable texture blit.</param>
/// <param name="blitMips">Blit mip maps.</param>
/// <returns>True on success, false otherwise.</returns>
public virtual bool UpdateTexture(CommandBuffer cmd, Texture texture, ref Vector4 scaleOffset, bool updateIfNeeded = true, bool blitMips = true)
=> UpdateTexture(cmd, texture, texture, ref scaleOffset, fullScaleOffset, updateIfNeeded, blitMips);
internal bool EnsureTextureSlot(out bool isUploadNeeded, ref Vector4 scaleBias, int key, int width, int height)
{
isUploadNeeded = false;
if (m_AllocationCache.TryGetValue(key, out var value))
{
scaleBias = value.scaleOffset;
return true;
}
if (!m_AtlasAllocator.Allocate(ref scaleBias, width, height))
return false;
isUploadNeeded = true;
scaleBias.Scale(new Vector4(1.0f / m_Width, 1.0f / m_Height, 1.0f / m_Width, 1.0f / m_Height));
m_AllocationCache.Add(key, (scaleBias, new Vector2Int(width, height)));
return true;
}
}
}

View File

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

View File

@@ -0,0 +1,514 @@
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using System;
using System.Runtime.InteropServices;
namespace UnityEngine.Rendering
{
class AtlasAllocatorDynamic
{
private class AtlasNodePool
{
internal AtlasNode[] m_Nodes;
Int16 m_Next;
Int16 m_FreelistHead;
public AtlasNodePool(Int16 capacity)
{
m_Nodes = new AtlasNode[capacity];
m_Next = 0;
m_FreelistHead = -1;
}
public void Dispose()
{
Clear();
m_Nodes = null;
}
public void Clear()
{
m_Next = 0;
m_FreelistHead = -1;
}
public Int16 AtlasNodeCreate(Int16 parent)
{
Debug.Assert((m_Next < m_Nodes.Length) || (m_FreelistHead != -1), "Error: AtlasNodePool: Out of memory. Please pre-allocate pool to larger capacity");
if (m_FreelistHead != -1)
{
Int16 freelistHeadNext = m_Nodes[m_FreelistHead].m_FreelistNext;
m_Nodes[m_FreelistHead] = new AtlasNode(m_FreelistHead, parent);
Int16 res = m_FreelistHead;
m_FreelistHead = freelistHeadNext;
return res;
}
m_Nodes[m_Next] = new AtlasNode(m_Next, parent);
return m_Next++;
}
public void AtlasNodeFree(Int16 index)
{
Debug.Assert(index >= 0 && index < m_Nodes.Length, "Error: AtlasNodeFree: index out of range.");
m_Nodes[index].m_FreelistNext = m_FreelistHead;
m_FreelistHead = index;
}
}
[StructLayout(LayoutKind.Explicit, Size = 32)]
private struct AtlasNode
{
private enum AtlasNodeFlags : uint
{
IsOccupied = 1 << 0
}
[FieldOffset(0)] public Int16 m_Self;
[FieldOffset(2)] public Int16 m_Parent;
[FieldOffset(4)] public Int16 m_LeftChild;
[FieldOffset(6)] public Int16 m_RightChild;
[FieldOffset(8)] public Int16 m_FreelistNext;
[FieldOffset(10)] public UInt16 m_Flags;
// [15:12] bytes are padding
[FieldOffset(16)] public Vector4 m_Rect;
public AtlasNode(Int16 self, Int16 parent)
{
m_Self = self;
m_Parent = parent;
m_LeftChild = -1;
m_RightChild = -1;
m_Flags = 0;
m_FreelistNext = -1;
m_Rect = Vector4.zero; // x,y is width and height (scale) z,w offset into atlas (bias)
}
public bool IsOccupied()
{
return (m_Flags & (UInt16)AtlasNodeFlags.IsOccupied) > 0;
}
public void SetIsOccupied()
{
UInt16 isOccupiedMask = (UInt16)AtlasNodeFlags.IsOccupied;
m_Flags |= isOccupiedMask;
}
public void ClearIsOccupied()
{
UInt16 isOccupiedMask = (UInt16)AtlasNodeFlags.IsOccupied;
m_Flags &= (UInt16)~isOccupiedMask;
}
public bool IsLeafNode()
{
// Note: Only need to check if m_LeftChild == null, as either both are allocated (split), or none are allocated (leaf).
return m_LeftChild == -1;
}
public Int16 Allocate(AtlasNodePool pool, int width, int height)
{
if (Mathf.Min(width, height) < 1)
{
// Degenerate allocation requested.
Debug.Assert(false, "Error: Texture2DAtlasDynamic: Attempted to allocate a degenerate region. Please ensure width and height are >= 1");
return -1;
}
// not a leaf node, try children
// TODO: Rather than always going left, then right, we might want to always attempt to allocate in the smaller child, then larger.
if (!IsLeafNode())
{
Int16 node = pool.m_Nodes[m_LeftChild].Allocate(pool, width, height);
if (node == -1)
{
node = pool.m_Nodes[m_RightChild].Allocate(pool, width, height);
}
return node;
}
// leaf node, check for fit
if (IsOccupied()) { return -1; }
if (width > m_Rect.x || height > m_Rect.y) { return -1; }
// perform the split
Debug.Assert(m_LeftChild == -1);
Debug.Assert(m_RightChild == -1);
m_LeftChild = pool.AtlasNodeCreate(m_Self);
m_RightChild = pool.AtlasNodeCreate(m_Self);
// Debug.Log("m_LeftChild = " + m_LeftChild);
// Debug.Log("m_RightChild = " + m_RightChild);
Debug.Assert(m_LeftChild >= 0 && m_LeftChild < pool.m_Nodes.Length);
Debug.Assert(m_RightChild >= 0 && m_RightChild < pool.m_Nodes.Length);
// Debug.Log("Rect = {" + m_Rect.x + ", " + m_Rect.y + ", " + m_Rect.z + ", " + m_Rect.w + "}");
float deltaX = m_Rect.x - width;
float deltaY = m_Rect.y - height;
// Debug.Log("deltaX = " + deltaX);
// Debug.Log("deltaY = " + deltaY);
if (deltaX >= deltaY)
{
// Debug.Log("Split horizontally");
// +--------+------+
// | | |
// | | |
// | | |
// | | |
// +--------+------+
pool.m_Nodes[m_LeftChild].m_Rect.x = width;
pool.m_Nodes[m_LeftChild].m_Rect.y = m_Rect.y;
pool.m_Nodes[m_LeftChild].m_Rect.z = m_Rect.z;
pool.m_Nodes[m_LeftChild].m_Rect.w = m_Rect.w;
pool.m_Nodes[m_RightChild].m_Rect.x = deltaX;
pool.m_Nodes[m_RightChild].m_Rect.y = m_Rect.y;
pool.m_Nodes[m_RightChild].m_Rect.z = m_Rect.z + width;
pool.m_Nodes[m_RightChild].m_Rect.w = m_Rect.w;
if (deltaY < 1)
{
pool.m_Nodes[m_LeftChild].SetIsOccupied();
return m_LeftChild;
}
else
{
Int16 node = pool.m_Nodes[m_LeftChild].Allocate(pool, width, height);
if (node >= 0) { pool.m_Nodes[node].SetIsOccupied(); }
return node;
}
}
else
{
// Debug.Log("Split vertically.");
// +---------------+
// | |
// |---------------|
// | |
// | |
// +---------------+
pool.m_Nodes[m_LeftChild].m_Rect.x = m_Rect.x;
pool.m_Nodes[m_LeftChild].m_Rect.y = height;
pool.m_Nodes[m_LeftChild].m_Rect.z = m_Rect.z;
pool.m_Nodes[m_LeftChild].m_Rect.w = m_Rect.w;
pool.m_Nodes[m_RightChild].m_Rect.x = m_Rect.x;
pool.m_Nodes[m_RightChild].m_Rect.y = deltaY;
pool.m_Nodes[m_RightChild].m_Rect.z = m_Rect.z;
pool.m_Nodes[m_RightChild].m_Rect.w = m_Rect.w + height;
if (deltaX < 1)
{
pool.m_Nodes[m_LeftChild].SetIsOccupied();
return m_LeftChild;
}
else
{
Int16 node = pool.m_Nodes[m_LeftChild].Allocate(pool, width, height);
if (node >= 0) { pool.m_Nodes[node].SetIsOccupied(); }
return node;
}
}
}
public void ReleaseChildren(AtlasNodePool pool)
{
if (IsLeafNode()) { return; }
pool.m_Nodes[m_LeftChild].ReleaseChildren(pool);
pool.m_Nodes[m_RightChild].ReleaseChildren(pool);
pool.AtlasNodeFree(m_LeftChild);
pool.AtlasNodeFree(m_RightChild);
m_LeftChild = -1;
m_RightChild = -1;
}
public void ReleaseAndMerge(AtlasNodePool pool)
{
Int16 n = m_Self;
do
{
pool.m_Nodes[n].ReleaseChildren(pool);
pool.m_Nodes[n].ClearIsOccupied();
n = pool.m_Nodes[n].m_Parent;
}
while (n >= 0 && pool.m_Nodes[n].IsMergeNeeded(pool));
}
public bool IsMergeNeeded(AtlasNodePool pool)
{
return pool.m_Nodes[m_LeftChild].IsLeafNode() && (!pool.m_Nodes[m_LeftChild].IsOccupied())
&& pool.m_Nodes[m_RightChild].IsLeafNode() && (!pool.m_Nodes[m_RightChild].IsOccupied());
}
}
private int m_Width;
private int m_Height;
private AtlasNodePool m_Pool;
private Int16 m_Root;
private Dictionary<int, Int16> m_NodeFromID;
public AtlasAllocatorDynamic(int width, int height, int capacityAllocations)
{
// In an evenly split binary tree, the nodeCount == leafNodeCount * 2
int capacityNodes = capacityAllocations * 2;
Debug.Assert(capacityNodes < (1 << 16), "Error: AtlasAllocatorDynamic: Attempted to allocate a capacity of " + capacityNodes + ", which is greater than our 16-bit indices can support. Please request a capacity <=" + (1 << 16));
m_Pool = new AtlasNodePool((Int16)capacityNodes);
m_NodeFromID = new Dictionary<int, Int16>(capacityAllocations);
Int16 rootParent = -1;
m_Root = m_Pool.AtlasNodeCreate(rootParent);
m_Pool.m_Nodes[m_Root].m_Rect.Set(width, height, 0, 0);
m_Width = width;
m_Height = height;
// string debug = "";
// DebugStringFromNode(ref debug, m_Root);
// Debug.Log("Allocating atlas = " + debug);
}
public bool Allocate(out Vector4 result, int key, int width, int height)
{
Int16 node = m_Pool.m_Nodes[m_Root].Allocate(m_Pool, width, height);
if (node >= 0)
{
result = m_Pool.m_Nodes[node].m_Rect;
m_NodeFromID.Add(key, node);
return true;
}
else
{
result = Vector4.zero;
return false;
}
}
public void Release(int key)
{
if (m_NodeFromID.TryGetValue(key, out Int16 node))
{
Debug.Assert(node >= 0 && node < m_Pool.m_Nodes.Length);
m_Pool.m_Nodes[node].ReleaseAndMerge(m_Pool);
m_NodeFromID.Remove(key);
return;
}
}
public void Release()
{
m_Pool.Clear();
m_Root = m_Pool.AtlasNodeCreate(-1);
m_Pool.m_Nodes[m_Root].m_Rect.Set(m_Width, m_Height, 0, 0);
m_NodeFromID.Clear();
}
public string DebugStringFromRoot(int depthMax = -1)
{
string res = "";
DebugStringFromNode(ref res, m_Root, 0, depthMax);
return res;
}
private void DebugStringFromNode(ref string res, Int16 n, int depthCurrent = 0, int depthMax = -1)
{
res += "{[" + depthCurrent + "], isOccupied = " + (m_Pool.m_Nodes[n].IsOccupied() ? "true" : "false") + ", self = " + m_Pool.m_Nodes[n].m_Self + ", " + m_Pool.m_Nodes[n].m_Rect.x + "," + m_Pool.m_Nodes[n].m_Rect.y + ", " + m_Pool.m_Nodes[n].m_Rect.z + ", " + m_Pool.m_Nodes[n].m_Rect.w + "}\n";
if (depthMax == -1 || depthCurrent < depthMax)
{
if (m_Pool.m_Nodes[n].m_LeftChild >= 0)
{
DebugStringFromNode(ref res, m_Pool.m_Nodes[n].m_LeftChild, depthCurrent + 1, depthMax);
}
if (m_Pool.m_Nodes[n].m_RightChild >= 0)
{
DebugStringFromNode(ref res, m_Pool.m_Nodes[n].m_RightChild, depthCurrent + 1, depthMax);
}
}
}
}
/// <summary>
/// A generic Atlas texture of 2D textures.
/// An atlas texture is a texture collection that collects multiple sub-textures into a single big texture.
/// Sub-texture allocation for Texture2DAtlasDynamic is dynamic.
/// </summary>
internal class Texture2DAtlasDynamic
{
private RTHandle m_AtlasTexture = null;
private bool isAtlasTextureOwner = false;
private int m_Width;
private int m_Height;
private GraphicsFormat m_Format;
private AtlasAllocatorDynamic m_AtlasAllocator = null;
private Dictionary<int, Vector4> m_AllocationCache;
/// <summary>
/// Handle to the texture of the atlas.
/// </summary>
public RTHandle AtlasTexture
{
get
{
return m_AtlasTexture;
}
}
/// <summary>
/// Creates a new empty texture atlas.
/// </summary>
/// <param name="width">Atlas width.</param>
/// <param name="height">Atlas height.</param>
/// <param name="capacity">Maximum number of items in the atlas.</param>
/// <param name="format">Atlas format.</param>
public Texture2DAtlasDynamic(int width, int height, int capacity, GraphicsFormat format)
{
m_Width = width;
m_Height = height;
m_Format = format;
m_AtlasTexture = RTHandles.Alloc(
m_Width,
m_Height,
1,
DepthBits.None,
m_Format,
FilterMode.Point,
TextureWrapMode.Clamp,
TextureDimension.Tex2D,
false,
true,
false,
false,
1,
0,
MSAASamples.None,
false,
false
);
isAtlasTextureOwner = true;
m_AtlasAllocator = new AtlasAllocatorDynamic(width, height, capacity);
m_AllocationCache = new Dictionary<int, Vector4>(capacity);
}
/// <summary>
/// Creates a new empty texture atlas. Use external atlas texture.
/// </summary>
/// <param name="width">Atlas width.</param>
/// <param name="height">Atlas height.</param>
/// <param name="capacity">Maximum number of items in the atlas.</param>
/// <param name="atlasTexture">Atlas texture.</param>
public Texture2DAtlasDynamic(int width, int height, int capacity, RTHandle atlasTexture)
{
m_Width = width;
m_Height = height;
m_Format = atlasTexture.rt.graphicsFormat;
m_AtlasTexture = atlasTexture;
isAtlasTextureOwner = false;
m_AtlasAllocator = new AtlasAllocatorDynamic(width, height, capacity);
m_AllocationCache = new Dictionary<int, Vector4>(capacity);
}
/// <summary>
/// Release atlas resources.
/// </summary>
public void Release()
{
ResetAllocator();
if (isAtlasTextureOwner) { RTHandles.Release(m_AtlasTexture); }
}
/// <summary>
/// Clear atlas sub-texture allocations.
/// </summary>
public void ResetAllocator()
{
m_AtlasAllocator.Release();
m_AllocationCache.Clear();
}
/// <summary>
/// Add a texture into the atlas.
/// </summary>
/// <param name="cmd">A command buffer Unity uses to upload the texture.</param>
/// <param name="scaleOffset">The texture rectangle coordinates in the atlas.</param>
/// <param name="texture">The texture added.</param>
/// <returns>Returns True if Unity successfully adds the texture.</returns>
public bool AddTexture(CommandBuffer cmd, out Vector4 scaleOffset, Texture texture)
{
int key = texture.GetInstanceID();
if (!m_AllocationCache.TryGetValue(key, out scaleOffset))
{
int width = texture.width;
int height = texture.height;
if (m_AtlasAllocator.Allocate(out scaleOffset, key, width, height))
{
scaleOffset.Scale(new Vector4(1.0f / m_Width, 1.0f / m_Height, 1.0f / m_Width, 1.0f / m_Height));
for (int mipLevel = 0; mipLevel < (texture as Texture2D).mipmapCount; mipLevel++)
{
cmd.SetRenderTarget(m_AtlasTexture, mipLevel);
Blitter.BlitQuad(cmd, texture, new Vector4(1, 1, 0, 0), scaleOffset, mipLevel, false);
}
m_AllocationCache.Add(key, scaleOffset);
return true;
}
else
{
return false;
}
}
return true;
}
/// <summary>
/// Check if the atlas contains the texture.
/// </summary>
/// <param name="scaleOffset">The texture rectangle coordinates in the atlas.</param>
/// <param name="key">The key that identifies the texture.</param>
/// <returns>Returns True if the texture is cached.</returns>
public bool IsCached(out Vector4 scaleOffset, int key)
{
return m_AllocationCache.TryGetValue(key, out scaleOffset);
}
/// <summary>
/// Allocate space from the atlas.
/// </summary>
/// <param name="isUploadNeeded">Wether the texture upload is needed after Unity ensures that the slot is allocated.</param>
/// <param name="scaleOffset">The texture rectangle coordinates in the atlas.</param>
/// <param name="key">The key that identifies the texture.</param>
/// <param name="width">Width of the texture.</param>
/// <param name="height">Height of the texture.</param>
/// <returns>Returns True if Unity successfully allocates the slot.</returns>
public bool EnsureTextureSlot(out bool isUploadNeeded, out Vector4 scaleOffset, int key, int width, int height)
{
isUploadNeeded = false;
if (m_AllocationCache.TryGetValue(key, out scaleOffset)) { return true; }
// Debug.Log("EnsureTextureSlot Before = " + m_AtlasAllocator.DebugStringFromRoot());
if (!m_AtlasAllocator.Allocate(out scaleOffset, key, width, height)) { return false; }
// Debug.Log("EnsureTextureSlot After = " + m_AtlasAllocator.DebugStringFromRoot());
isUploadNeeded = true;
scaleOffset.Scale(new Vector4(1.0f / m_Width, 1.0f / m_Height, 1.0f / m_Width, 1.0f / m_Height));
m_AllocationCache.Add(key, scaleOffset);
return true;
}
/// <summary>
/// Release allocated space from the atlas.
/// </summary>
/// <param name="key">The key that identifies the texture.</param>
public void ReleaseTextureSlot(int key)
{
m_AtlasAllocator.Release(key);
m_AllocationCache.Remove(key);
}
}
}

View File

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

View File

@@ -0,0 +1,253 @@
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// Utility class providing default textures compatible in any XR setup.
/// </summary>
public static class TextureXR
{
// Property set by XRSystem
private static int m_MaxViews = 1;
/// <summary>
/// Maximum number of views handled by the XR system.
/// </summary>
public static int maxViews
{
set
{
m_MaxViews = value;
}
}
// Property accessed when allocating a render target
/// <summary>
/// Number of slices used by the XR system.
/// </summary>
public static int slices { get => m_MaxViews; }
// Must be in sync with shader define in TextureXR.hlsl
/// <summary>
/// Returns true if the XR system uses texture arrays.
/// </summary>
public static bool useTexArray
{
get
{
switch (SystemInfo.graphicsDeviceType)
{
case GraphicsDeviceType.Direct3D11:
case GraphicsDeviceType.Direct3D12:
case GraphicsDeviceType.PlayStation4:
case GraphicsDeviceType.PlayStation5:
case GraphicsDeviceType.PlayStation5NGGC:
case GraphicsDeviceType.Vulkan:
case GraphicsDeviceType.Metal:
return true;
default:
return false;
}
}
}
/// <summary>
/// Dimension of XR textures.
/// </summary>
public static TextureDimension dimension
{
get
{
// TEXTURE2D_X macros will now expand to TEXTURE2D or TEXTURE2D_ARRAY
return useTexArray ? TextureDimension.Tex2DArray : TextureDimension.Tex2D;
}
}
// Need to keep both the Texture and the RTHandle in order to be able to track lifetime properly.
static Texture m_BlackUIntTexture2DArray;
static Texture m_BlackUIntTexture;
static RTHandle m_BlackUIntTexture2DArrayRTH;
static RTHandle m_BlackUIntTextureRTH;
/// <summary>
/// Default black unsigned integer texture.
/// </summary>
/// <returns>The default black unsigned integer texture.</returns>
public static RTHandle GetBlackUIntTexture() { return useTexArray ? m_BlackUIntTexture2DArrayRTH : m_BlackUIntTextureRTH; }
static Texture2DArray m_ClearTexture2DArray;
static Texture2D m_ClearTexture;
static RTHandle m_ClearTexture2DArrayRTH;
static RTHandle m_ClearTextureRTH;
/// <summary>
/// Default clear color (0, 0, 0, 1) texture.
/// </summary>
/// <returns>The default clear color texture.</returns>
public static RTHandle GetClearTexture() { return useTexArray ? m_ClearTexture2DArrayRTH : m_ClearTextureRTH; }
static Texture2DArray m_MagentaTexture2DArray;
static Texture2D m_MagentaTexture;
static RTHandle m_MagentaTexture2DArrayRTH;
static RTHandle m_MagentaTextureRTH;
/// <summary>
/// Default magenta texture.
/// </summary>
/// <returns>The default magenta texture.</returns>
public static RTHandle GetMagentaTexture() { return useTexArray ? m_MagentaTexture2DArrayRTH : m_MagentaTextureRTH; }
static Texture2D m_BlackTexture;
static Texture3D m_BlackTexture3D;
static Texture2DArray m_BlackTexture2DArray;
static RTHandle m_BlackTexture2DArrayRTH;
static RTHandle m_BlackTextureRTH;
static RTHandle m_BlackTexture3DRTH;
/// <summary>
/// Default black texture.
/// </summary>
/// <returns>The default black texture.</returns>
public static RTHandle GetBlackTexture() { return useTexArray ? m_BlackTexture2DArrayRTH : m_BlackTextureRTH; }
/// <summary>
/// Default black texture array.
/// </summary>
/// <returns>The default black texture array.</returns>
public static RTHandle GetBlackTextureArray() { return m_BlackTexture2DArrayRTH; }
/// <summary>
/// Default black texture 3D.
/// </summary>
/// <returns>The default black texture 3D.</returns>
public static RTHandle GetBlackTexture3D() { return m_BlackTexture3DRTH; }
static Texture2DArray m_WhiteTexture2DArray;
static RTHandle m_WhiteTexture2DArrayRTH;
static RTHandle m_WhiteTextureRTH;
/// <summary>
/// Default white texture.
/// </summary>
/// <returns>The default white texture.</returns>
public static RTHandle GetWhiteTexture() { return useTexArray ? m_WhiteTexture2DArrayRTH : m_WhiteTextureRTH; }
/// <summary>
/// Initialize XR textures. Must be called at least once.
/// </summary>
/// <param name="cmd">Command Buffer used to initialize textures.</param>
/// <param name="clearR32_UIntShader">Compute shader used to intitialize unsigned integer textures.</param>
public static void Initialize(CommandBuffer cmd, ComputeShader clearR32_UIntShader)
{
if (m_BlackUIntTexture2DArray == null) // We assume that everything is invalid if one is invalid.
{
// Black UINT
RTHandles.Release(m_BlackUIntTexture2DArrayRTH);
m_BlackUIntTexture2DArray = CreateBlackUIntTextureArray(cmd, clearR32_UIntShader);
m_BlackUIntTexture2DArrayRTH = RTHandles.Alloc(m_BlackUIntTexture2DArray);
RTHandles.Release(m_BlackUIntTextureRTH);
m_BlackUIntTexture = CreateBlackUintTexture(cmd, clearR32_UIntShader);
m_BlackUIntTextureRTH = RTHandles.Alloc(m_BlackUIntTexture);
// Clear
RTHandles.Release(m_ClearTextureRTH);
m_ClearTexture = new Texture2D(1, 1, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.None) { name = "Clear Texture" };
m_ClearTexture.SetPixel(0, 0, Color.clear);
m_ClearTexture.Apply();
m_ClearTextureRTH = RTHandles.Alloc(m_ClearTexture);
RTHandles.Release(m_ClearTexture2DArrayRTH);
m_ClearTexture2DArray = CreateTexture2DArrayFromTexture2D(m_ClearTexture, "Clear Texture2DArray");
m_ClearTexture2DArrayRTH = RTHandles.Alloc(m_ClearTexture2DArray);
// Magenta
RTHandles.Release(m_MagentaTextureRTH);
m_MagentaTexture = new Texture2D(1, 1, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.None) { name = "Magenta Texture" };
m_MagentaTexture.SetPixel(0, 0, Color.magenta);
m_MagentaTexture.Apply();
m_MagentaTextureRTH = RTHandles.Alloc(m_MagentaTexture);
RTHandles.Release(m_MagentaTexture2DArrayRTH);
m_MagentaTexture2DArray = CreateTexture2DArrayFromTexture2D(m_MagentaTexture, "Magenta Texture2DArray");
m_MagentaTexture2DArrayRTH = RTHandles.Alloc(m_MagentaTexture2DArray);
// Black
RTHandles.Release(m_BlackTextureRTH);
m_BlackTexture = new Texture2D(1, 1, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.None) { name = "Black Texture" };
m_BlackTexture.SetPixel(0, 0, Color.black);
m_BlackTexture.Apply();
m_BlackTextureRTH = RTHandles.Alloc(m_BlackTexture);
RTHandles.Release(m_BlackTexture2DArrayRTH);
m_BlackTexture2DArray = CreateTexture2DArrayFromTexture2D(m_BlackTexture, "Black Texture2DArray");
m_BlackTexture2DArrayRTH = RTHandles.Alloc(m_BlackTexture2DArray);
RTHandles.Release(m_BlackTexture3DRTH);
m_BlackTexture3D = CreateBlackTexture3D("Black Texture3D");
m_BlackTexture3DRTH = RTHandles.Alloc(m_BlackTexture3D);
// White
RTHandles.Release(m_WhiteTextureRTH);
m_WhiteTextureRTH = RTHandles.Alloc(Texture2D.whiteTexture);
RTHandles.Release(m_WhiteTexture2DArrayRTH);
m_WhiteTexture2DArray = CreateTexture2DArrayFromTexture2D(Texture2D.whiteTexture, "White Texture2DArray");
m_WhiteTexture2DArrayRTH = RTHandles.Alloc(m_WhiteTexture2DArray);
}
}
static Texture2DArray CreateTexture2DArrayFromTexture2D(Texture2D source, string name)
{
Texture2DArray texArray = new Texture2DArray(source.width, source.height, slices, source.format, false) { name = name };
for (int i = 0; i < slices; ++i)
Graphics.CopyTexture(source, 0, 0, texArray, i, 0);
return texArray;
}
static Texture CreateBlackUIntTextureArray(CommandBuffer cmd, ComputeShader clearR32_UIntShader)
{
RenderTexture blackUIntTexture2DArray = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt)
{
dimension = TextureDimension.Tex2DArray,
volumeDepth = slices,
useMipMap = false,
autoGenerateMips = false,
enableRandomWrite = true,
name = "Black UInt Texture Array"
};
blackUIntTexture2DArray.Create();
// Workaround because we currently can't create a Texture2DArray using an R32_UInt format
// So we create a R32_UInt RenderTarget and clear it using a compute shader, because we can't
// Clear this type of target on metal devices (output type nor compatible: float4 vs uint)
int kernel = clearR32_UIntShader.FindKernel("ClearUIntTextureArray");
cmd.SetComputeTextureParam(clearR32_UIntShader, kernel, "_TargetArray", blackUIntTexture2DArray);
cmd.DispatchCompute(clearR32_UIntShader, kernel, 1, 1, slices);
return blackUIntTexture2DArray as Texture;
}
static Texture CreateBlackUintTexture(CommandBuffer cmd, ComputeShader clearR32_UIntShader)
{
RenderTexture blackUIntTexture2D = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt)
{
dimension = TextureDimension.Tex2D,
volumeDepth = slices,
useMipMap = false,
autoGenerateMips = false,
enableRandomWrite = true,
name = "Black UInt Texture Array"
};
blackUIntTexture2D.Create();
// Workaround because we currently can't create a Texture2DArray using an R32_UInt format
// So we create a R32_UInt RenderTarget and clear it using a compute shader, because we can't
// Clear this type of target on metal devices (output type nor compatible: float4 vs uint)
int kernel = clearR32_UIntShader.FindKernel("ClearUIntTexture");
cmd.SetComputeTextureParam(clearR32_UIntShader, kernel, "_Target", blackUIntTexture2D);
cmd.DispatchCompute(clearR32_UIntShader, kernel, 1, 1, slices);
return blackUIntTexture2D as Texture;
}
static Texture3D CreateBlackTexture3D(string name)
{
Texture3D texture3D = new Texture3D(width: 1, height: 1, depth: 1, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.None);
texture3D.name = name;
texture3D.SetPixel(0, 0, 0, Color.black, 0);
texture3D.Apply(updateMipmaps: false);
return texture3D;
}
}
}

View File

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