using System; using System.Runtime.CompilerServices; using Unity.Mathematics; namespace UnityEngine.Rendering.RenderGraphModule.Util { /// /// Helper functions used for RenderGraph. /// public static partial class RenderGraphUtils { static MaterialPropertyBlock s_PropertyBlock = new MaterialPropertyBlock(); /// /// Checks if the shader features required by the MSAA version of the copy pass is supported on current platform. /// /// Returns true if the shader features required by the copy pass is supported for MSAA, otherwise will it return false. public static bool CanAddCopyPassMSAA() { return Blitter.CanCopyMSAA(); } class CopyPassData { public bool isMSAA; } /// /// Adds a pass to copy data from a source texture to a destination texture. The data in the texture is copied pixel by pixel. The copy function can only do 1:1 copies it will not allow scaling the data or /// doing texture filtering. Furthermore it requires the source and destination surfaces to be the same size in pixels and have the same number of MSAA samples. If the textures are multi sampled /// individual samples will be copied. /// /// Copy is intentionally limited in functionally so it can be implemented using frame buffer fetch for optimal performance on tile based GPUs. If you are looking for a more generic /// function please use the AddBlitPass function. /// /// For XR textures you will have to copy for each eye seperatly. /// /// For MSAA textures please use the CanAddCopyPassMSAA() function first to check if the CopyPass is supported on current platform. /// /// /// The RenderGraph adding this pass to. /// The texture the data is copied from. /// The texture the data is copied to. This has to be different from souce. /// The source slice of a Array of 3D texture to use. Must be 0 for regular 2D textures. /// The destination slice of a Array of 3D texture to use. Must be 0 for regular 2D textures. /// The first mipmap level to copy from. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. /// The first mipmap level to copy to. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. /// A name to use for debugging and error logging. This name will be shown in the rendergraph debugger. /// File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it. /// File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it. public static void AddCopyPass( this RenderGraph graph, TextureHandle source, TextureHandle destination, int sourceSlice = 0, int destinationSlice = 0, int sourceMip = 0, int destinationMip = 0, string passName = "Copy Pass Utility" #if !CORE_PACKAGE_DOCTOOLS , [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) #endif { if (!graph.nativeRenderPassesEnabled) throw new ArgumentException("CopyPass only supported for native render pass. Please use the blit functions instead for non native render pass platforms."); var sourceDesc = graph.GetTextureDesc(source); var destinationDesc = graph.GetTextureDesc(destination); if (sourceSlice < 0 || sourceSlice >= sourceDesc.slices) throw new ArgumentException("Invalid sourceSlice."); if (destinationSlice < 0 || destinationSlice >= destinationDesc.slices) throw new ArgumentException("Invalid destinationSlice."); int sourceMaxWidth = math.max(math.max(sourceDesc.width, sourceDesc.height), sourceDesc.slices); int sourceTotalMipChainLevels = (int)math.log2(sourceMaxWidth) + 1; if (sourceMip < 0 || sourceMip >= sourceMaxWidth) throw new ArgumentException("Invalid sourceMip."); int destinationMaxWidth = math.max(math.max(destinationDesc.width, destinationDesc.height), destinationDesc.slices); int destinationTotalMipChainLevels = (int)math.log2(destinationMaxWidth) + 1; if (destinationMip < 0 || destinationMip >= destinationMaxWidth) throw new ArgumentException("Invalid destinationMip."); if (sourceDesc.msaaSamples != destinationDesc.msaaSamples) throw new ArgumentException("MSAA samples from source and destination texture doesn't match."); var isMSAA = (int)sourceDesc.msaaSamples > 1; // Note: Needs shader model ps_4.1 to support SV_SampleIndex which means the copy pass isn't supported for MSAA on some platforms. // We can check this by checking the amout of shader passes the copy shader has. // It would have 1 if the MSAA pass is not able to be used for target and 2 otherwise. // https://docs.unity3d.com/2017.4/Documentation/Manual/SL-ShaderCompileTargets.html // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-get-sample-position if (isMSAA && !Blitter.CanCopyMSAA()) throw new ArgumentException("Target does not support MSAA for AddCopyPass. Please use the blit alternative or use non MSAA textures."); using (var builder = graph.AddRasterRenderPass(passName, out var passData, file, line)) { passData.isMSAA = isMSAA; builder.SetInputAttachment(source, 0, AccessFlags.Read, sourceMip, sourceSlice); builder.SetRenderAttachment(destination, 0, AccessFlags.Write, destinationMip, destinationSlice); builder.SetRenderFunc((CopyPassData data, RasterGraphContext context) => CopyRenderFunc(data, context)); } } static void CopyRenderFunc(CopyPassData data, RasterGraphContext rgContext) { Blitter.CopyTexture(rgContext.cmd, data.isMSAA); } /// /// Filtermode for the simple blit. /// public enum BlitFilterMode { /// /// Clamp to the nearest pixel when selecting which pixel to blit from. /// ClampNearest, /// /// Use bileanear filtering when selecting which pixels to blit from. /// ClampBilinear } class BlitPassData { public TextureHandle source; public TextureHandle destination; public Vector2 scale; public Vector2 offset; public int sourceSlice; public int destinationSlice; public int numSlices; public int sourceMip; public int destinationMip; public int numMips; public BlitFilterMode filterMode; } /// /// Add a render graph pass to blit an area of the source texture into the destination texture. Blitting is a high-level way to transfer texture data from a source to a destination texture. /// It may scale and texture-filter the transferred data as well as doing data transformations on it (e.g. R8Unorm to float). /// /// This function does not have special handling for MSAA textures. This means that when the source is sampled this will be a resolved value (standard Unity behavior when sampling an MSAA render texture) /// and when the destination is MSAA all written samples will contain the same values (e.g. as you would expect when rendering a full screen quad to an msaa buffer). If you need special MSAA /// handling or custom resolving please use the overload that takes a Material and implement the appropriate behavior in the shader. /// /// This function works transparently with regular textures and XR textures (which may depending on the situation be 2D array textures). In the case of an XR array texture /// the operation will be repeated for each slice in the texture if numSlices is set to -1. /// /// /// The RenderGraph adding this pass to. /// The texture the data is copied from. /// The texture the data is copied to. /// The scale that is applied to the texture coordinates used to sample the source texture. /// The offset that is added to the texture coordinates used to sample the source texture. /// The first slice to copy from if the texture is an 3D or array texture. Must be zero for regular textures. /// The first slice to copy to if the texture is an 3D or array texture. Must be zero for regular textures. /// The number of slices to copy. -1 to copy all slices until the end of the texture. Arguments that copy invalid slices to be copied will lead to an error. /// The first mipmap level to copy from. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. /// The first mipmap level to copy to. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. /// The number of mipmaps to copy. -1 to copy all mipmaps. Arguments that copy invalid mips to be copied will lead to an error. /// The filtering used when blitting from source to destination. /// A name to use for debugging and error logging. This name will be shown in the rendergraph debugger. /// File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it. /// File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it. public static void AddBlitPass(this RenderGraph graph, TextureHandle source, TextureHandle destination, Vector2 scale, Vector2 offset, int sourceSlice = 0, int destinationSlice = 0, int numSlices = -1, int sourceMip = 0, int destinationMip = 0, int numMips = 1, BlitFilterMode filterMode = BlitFilterMode.ClampBilinear, string passName = "Blit Pass Utility" #if !CORE_PACKAGE_DOCTOOLS , [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) #endif { var sourceDesc = graph.GetTextureDesc(source); var destinationDesc = graph.GetTextureDesc(destination); int sourceMaxWidth = math.max(math.max(sourceDesc.width, sourceDesc.height), sourceDesc.slices); int sourceTotalMipChainLevels = (int)math.log2(sourceMaxWidth) + 1; int destinationMaxWidth = math.max(math.max(destinationDesc.width, destinationDesc.height), destinationDesc.slices); int destinationTotalMipChainLevels = (int)math.log2(destinationMaxWidth) + 1; if (numSlices == -1) numSlices = sourceDesc.slices - sourceSlice; if (numSlices > sourceDesc.slices - sourceSlice || numSlices > destinationDesc.slices - destinationSlice) { throw new ArgumentException($"BlitPass: {passName} attempts to blit too many slices. The pass will be skipped."); } if (numMips == -1) numMips = sourceTotalMipChainLevels - sourceMip; if (numMips > sourceTotalMipChainLevels - sourceMip || numMips > destinationTotalMipChainLevels - destinationMip) { throw new ArgumentException($"BlitPass: {passName} attempts to blit too many mips. The pass will be skipped."); } using (var builder = graph.AddUnsafePass(passName, out var passData, file, line)) { passData.source = source; passData.destination = destination; passData.scale = scale; passData.offset = offset; passData.sourceSlice = sourceSlice; passData.destinationSlice = destinationSlice; passData.numSlices = numSlices; passData.sourceMip = sourceMip; passData.destinationMip = destinationMip; passData.numMips = numMips; passData.filterMode = filterMode; builder.UseTexture(source, AccessFlags.Read); builder.UseTexture(destination, AccessFlags.Write); builder.SetRenderFunc((BlitPassData data, UnsafeGraphContext context) => BlitRenderFunc(data, context)); } } static Vector4 s_BlitScaleBias = new Vector4(); static void BlitRenderFunc(BlitPassData data, UnsafeGraphContext context) { s_BlitScaleBias.x = data.scale.x; s_BlitScaleBias.y = data.scale.y; s_BlitScaleBias.z = data.offset.x; s_BlitScaleBias.w = data.offset.y; CommandBuffer unsafeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd); for (int currSlice = 0; currSlice < data.numSlices; currSlice++) { for (int currMip = 0; currMip < data.numMips; currMip++) { context.cmd.SetRenderTarget(data.destination, data.destinationMip + currMip, CubemapFace.Unknown, data.destinationSlice + currSlice); Blitter.BlitTexture(unsafeCmd, data.source, s_BlitScaleBias, data.sourceMip + currMip, data.sourceSlice + currSlice, data.filterMode == BlitFilterMode.ClampBilinear); } } } /// /// Enums to select what geometry used for the blit. /// public enum FullScreenGeometryType { /// /// Draw a quad mesh built of two triangles. The texture coordinates of the mesh will cover the 0-1 texture space. This is the most compatible if you have an existing Unity Graphics.Blit shader. /// This geometry allows you to use a simples vertex shader but has more rendering overhead on the CPU as a mesh and vertex buffers need to be bound to the pipeline. /// Mesh, /// /// A single triangle will be scheduled. The vertex shader will need to generate correct vertex data in the vertex shader for the tree vertices to cover the full screen. /// To get the vertices in the vertex shader include "com.unity.render-pipelines.core\ShaderLibrary\Common.hlsl" and call the GetFullScreenTriangleTexCoord/GetFullScreenTriangleVertexPosition /// ProceduralTriangle, /// /// A four vertices forming two triangles will be scheduled. The vertex shader will need to generate correct vertex data in the vertex shader for the four vertices to cover the full screen. /// While more intuitive this may be slower as the quad occupancy will be lower alongside the diagonal line where the two triangles meet. /// To get the vertices in the vertex shader include "com.unity.render-pipelines.core\ShaderLibrary\Common.hlsl" and call the GetQuadTexCoord/GetQuadVertexPosition /// ProceduralQuad, } /// /// This struct specifies all the arugments to the blit-with-material function. As there are many parameters with some of them only rarely used moving them to a struct /// makes it easier to use the function. /// /// Use one of the constructor overloads for common use cases. /// /// /// The shader properties defined in the struct or constructors is used for most common usecases but they are not required to be used in the shader. /// By using the MaterialPropertyBlock can you add your shader properties with custom values. /// public struct BlitMaterialParameters { private static readonly int blitTextureProperty = Shader.PropertyToID("_BlitTexture"); private static readonly int blitSliceProperty = Shader.PropertyToID("_BlitTexArraySlice"); private static readonly int blitMipProperty = Shader.PropertyToID("_BlitMipLevel"); private static readonly int blitScaleBias = Shader.PropertyToID("_BlitScaleBias"); /// /// Simple constructor to set a few amount of parameters to blit. /// /// The texture the data is copied from. /// The texture the data is copied to. /// Material used for blitting. /// The shader pass index to use for the material. public BlitMaterialParameters(TextureHandle source, TextureHandle destination, Material material, int shaderPass) : this(source, destination, Vector2.one, Vector2.zero, material, shaderPass) { } /// /// Simple constructor to set a few amount of parameters to blit. /// /// The texture the data is copied from. /// The texture the data is copied to. /// Scale for sampling the input texture. /// Offset also known as bias for sampling the input texture /// Material used for blitting. /// The shader pass index to use for the material. public BlitMaterialParameters(TextureHandle source, TextureHandle destination, Vector2 scale, Vector2 offset, Material material, int shaderPass) { this.source = source; this.destination = destination; this.scale = scale; this.offset = offset; sourceSlice = -1; destinationSlice = 0; numSlices = 1; sourceMip = -1; destinationMip = 0; numMips = 1; this.material = material; this.shaderPass = shaderPass; propertyBlock = null; sourceTexturePropertyID = blitTextureProperty; sourceSlicePropertyID = blitSliceProperty; sourceMipPropertyID = blitMipProperty; scaleBiasPropertyID = blitScaleBias; geometry = FullScreenGeometryType.ProceduralTriangle; } /// /// Constructor to set the source and destination mip and slices as well as material property and IDs to interact with it. /// /// The texture the data is copied from. /// The texture the data is copied to. /// Material used for blitting. /// The shader pass index to use for the material. /// Material property block to use to render the blit. This property should contain all data the shader needs. /// The first slice to copy to if the texture is an 3D or array texture. Must be zero for regular textures. /// The first mipmap level to copy to. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. /// The number of slices to copy. -1 to copy all slices until the end of the texture. Arguments that copy invalid slices to be copied will lead to an error. /// The number of mipmaps to copy. -1 to copy all mipmaps. Arguments that copy invalid mips to be copied will lead to an error. /// The first slice to copy from if the texture is an 3D or array texture. Must be zero for regular textures. Default is set to -1 to ignore source slices and set it to 0 without looping for each destination slice /// The first mipmap level to copy from. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. Defaults to -1 to ignore source mips and set it to 0 without looping for each destination mip. /// Geometry used for blitting the source texture. /// /// The texture property to set with the source texture. If -1 the default "_BlitTexture" texture property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If propertyBlock is null the texture will be applied directly to the material. /// /// /// The scalar property to set with the source slice index. If -1 the default "_BlitTexArraySlice" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one slice is rendered using the blit function (numSlices>1) several full screen quads will be rendered for each slice with different sourceSlicePropertyID values set. /// /// /// The scalar property to set with the source mip index. If -1 the default "_BlitMipLevel" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one mip is rendered using the blit function (numMips>1) several full screen quads will be rendered for each slice with different sourceMipPropertyID values set. /// public BlitMaterialParameters(TextureHandle source, TextureHandle destination, Material material, int shaderPass, MaterialPropertyBlock mpb, int destinationSlice, int destinationMip, int numSlices = 1, int numMips = 1, int sourceSlice = -1, int sourceMip = -1, FullScreenGeometryType geometry = FullScreenGeometryType.Mesh, int sourceTexturePropertyID = -1, int sourceSlicePropertyID = -1, int sourceMipPropertyID = -1) : this(source, destination, Vector2.one, Vector2.zero, material, shaderPass, mpb, destinationSlice, destinationMip, numSlices, numMips, sourceSlice, sourceMip, geometry, sourceTexturePropertyID, sourceSlicePropertyID, sourceMipPropertyID) { } /// /// Constructor to set the source and destination mip and slices as well as material property and IDs to interact with it. /// /// The texture the data is copied from. /// The texture the data is copied to. /// Scale for sampling the input texture. /// Offset also known as bias for sampling the input texture /// Material used for blitting. /// The shader pass index to use for the material. /// Material property block to use to render the blit. This property should contain all data the shader needs. /// The first slice to copy to if the texture is an 3D or array texture. Must be zero for regular textures. /// The first mipmap level to copy to. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. /// The number of slices to copy. -1 to copy all slices until the end of the texture. Arguments that copy invalid slices to be copied will lead to an error. /// The number of mipmaps to copy. -1 to copy all mipmaps. Arguments that copy invalid mips to be copied will lead to an error. /// The first slice to copy from if the texture is an 3D or array texture. Must be zero for regular textures. Default is set to -1 to ignore source slices and set it to 0 without looping for each destination slice /// The first mipmap level to copy from. Must be zero for non-mipmapped textures. Must be a valid index for mipmapped textures. Defaults to -1 to ignore source mips and set it to 0 without looping for each destination mip. /// Geometry used for blitting the source texture. /// /// The texture property to set with the source texture. If -1 the default "_BlitTexture" texture property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If propertyBlock is null the texture will be applied directly to the material. /// /// /// The scalar property to set with the source slice index. If -1 the default "_BlitTexArraySlice" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one slice is rendered using the blit function (numSlices>1) several full screen quads will be rendered for each slice with different sourceSlicePropertyID values set. /// /// /// The scalar property to set with the source mip index. If -1 the default "_BlitMipLevel" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one mip is rendered using the blit function (numMips>1) several full screen quads will be rendered for each slice with different sourceMipPropertyID values set. /// /// /// The scalar property to set with the scale and bias known as offset. If -1 the default "_BlitScaleBias" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// public BlitMaterialParameters(TextureHandle source, TextureHandle destination, Vector2 scale, Vector2 offset, Material material, int shaderPass, MaterialPropertyBlock mpb, int destinationSlice, int destinationMip, int numSlices = 1, int numMips = 1, int sourceSlice = -1, int sourceMip = -1, FullScreenGeometryType geometry = FullScreenGeometryType.Mesh, int sourceTexturePropertyID = -1, int sourceSlicePropertyID = -1, int sourceMipPropertyID = -1, int scaleBiasPropertyID = -1) : this(source, destination, scale, offset, material, shaderPass) { this.propertyBlock = mpb; this.sourceSlice = sourceSlice; this.destinationSlice = destinationSlice; this.numSlices = numSlices; this.sourceMip = sourceMip; this.destinationMip = destinationMip; this.numMips = numMips; if (sourceTexturePropertyID != -1) this.sourceTexturePropertyID = sourceTexturePropertyID; if (sourceSlicePropertyID != -1) this.sourceSlicePropertyID = sourceSlicePropertyID; if (sourceMipPropertyID != -1) this.sourceMipPropertyID = sourceMipPropertyID; if (scaleBiasPropertyID != -1) this.scaleBiasPropertyID = scaleBiasPropertyID; this.geometry = geometry; } /// /// Constructor to set textures, material, shader pass and material property block. /// /// The texture the data is copied from. /// The texture the data is copied to. /// Material used for blitting. /// The shader pass index to use for the material. /// Material property block to use to render the blit. This property should contain all data the shader needs. /// Geometry used for blitting the source texture. /// /// The texture property to set with the source texture. If -1 the default "_BlitTexture" texture property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If propertyBlock is null the texture will be applied directly to the material. /// /// /// The scalar property to set with the source slice index. If -1 the default "_BlitTexArraySlice" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one slice is rendered using the blit function (numSlices>1) several full screen quads will be rendered for each slice with different sourceSlicePropertyID values set. /// /// /// The scalar property to set with the source mip index. If -1 the default "_BlitMipLevel" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one mip is rendered using the blit function (numMips>1) several full screen quads will be rendered for each slice with different sourceMipPropertyID values set. /// public BlitMaterialParameters(TextureHandle source, TextureHandle destination, Material material, int shaderPass, MaterialPropertyBlock mpb, FullScreenGeometryType geometry = FullScreenGeometryType.Mesh, int sourceTexturePropertyID = -1, int sourceSlicePropertyID = -1, int sourceMipPropertyID = -1) : this(source, destination, Vector2.one, Vector2.zero, material, shaderPass, mpb, geometry, sourceTexturePropertyID, sourceSlicePropertyID, sourceMipPropertyID) { } /// /// Constructor to set textures, material, shader pass and material property block. /// /// The texture the data is copied from. /// The texture the data is copied to. /// Scale for sampling the input texture. /// Offset also known as bias for sampling the input texture /// Material used for blitting. /// The shader pass index to use for the material. /// Material property block to use to render the blit. This property should contain all data the shader needs. /// Geometry used for blitting the source texture. /// /// The texture property to set with the source texture. If -1 the default "_BlitTexture" texture property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If propertyBlock is null the texture will be applied directly to the material. /// /// /// The scalar property to set with the source slice index. If -1 the default "_BlitSlice" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one slice is rendered using the blit function (numSlices>1) several full screen quads will be rendered for each slice with different sourceSlicePropertyID values set. /// /// /// The scalar property to set with the source mip index. If -1 the default "_BlitMipLevel" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one mip is rendered using the blit function (numMips>1) several full screen quads will be rendered for each slice with different sourceMipPropertyID values set. /// /// /// The scalar property to set with the scale and bias known as offset. If -1 the default "_BlitScaleBias" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// public BlitMaterialParameters(TextureHandle source, TextureHandle destination, Vector2 scale, Vector2 offset, Material material, int shaderPass, MaterialPropertyBlock mpb, FullScreenGeometryType geometry = FullScreenGeometryType.Mesh, int sourceTexturePropertyID = -1, int sourceSlicePropertyID = -1, int sourceMipPropertyID = -1, int scaleBiasPropertyID = -1) : this(source, destination, scale, offset, material, shaderPass) { this.propertyBlock = mpb; if (sourceTexturePropertyID != -1) this.sourceTexturePropertyID = sourceTexturePropertyID; if (sourceSlicePropertyID != -1) this.sourceSlicePropertyID = sourceSlicePropertyID; if (sourceMipPropertyID != -1) this.sourceMipPropertyID = sourceMipPropertyID; if (scaleBiasPropertyID != -1) this.scaleBiasPropertyID = scaleBiasPropertyID; this.geometry = geometry; } /// /// The source texture. This texture will be set on the specified material property block property with /// the name specified sourceTexturePropertyID. If the property block is null, a temp property block will /// be allocated by the blit function. /// public TextureHandle source; /// /// The texture to blit into. This subresources (mips,slices) of this texture texture will be set-up as a render attachment based on the destination argumments. /// public TextureHandle destination; /// /// The scale used for the blit operation. /// public Vector2 scale; /// /// The offset of the blit destination. /// public Vector2 offset; /// /// The first slice of the source texture to blit from. -1 to ignore source slices and set it to 0 without looping for each destination slice. /// public int sourceSlice; /// /// The first slice of the destination texture to blit into. /// public int destinationSlice; /// /// The number of slices to blit. -1 to blit all slices until the end of the texture starting from destinationSlice. Arguments that copy invalid slices (e.g. out of range or zero) will lead to an error. /// public int numSlices; /// /// The first source mipmap to blit from. -1 to ignore source mips and set it to 0 without looping for each destination mip. /// public int sourceMip; /// /// The first destination mipmap to blit into. /// public int destinationMip; /// /// The number of mipmaps to blit. -1 to blit all mipmaps until the end of the texture starting from destinationMip. Arguments that copy invalid slices (e.g. out of range or zero) will lead to an error. /// public int numMips; /// /// The material to use, cannot be null. The blit functions will not modify this material in any way. /// public Material material; /// /// The shader pass index to use. /// public int shaderPass; /// /// The material propery block to use, can be null. The blit functions will modify the sourceTexturePropertyID, sourceSliceProperty, and sourceMipPropertyID of this material poperty block as part of the blit. /// Calling propertyBlock's SetTexture(...) function used by BlitMaterialParameters should be avoid since it will cause untracked textures when using RenderGraph. This can cause unexpected behaviours. /// public MaterialPropertyBlock propertyBlock; /// /// The texture property to set with the source texture. If -1 the default "_BlitTexture" texture property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If propertyBlock is null the texture will be applied directly to the material. /// public int sourceTexturePropertyID; /// /// The scalar property to set with the source slice index. If -1 the default "_BlitTexArraySlice" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one slice is rendered using the blit function (numSlices>1) several full screen quads will be rendered for each slice with different sourceSlicePropertyID values set. /// public int sourceSlicePropertyID; /// /// The scalar property to set with the source mip index. If -1 the default "_BlitMipLevel" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// If more than one mip is rendered using the blit function (numMips>1) several full screen quads will be rendered for each slice with different sourceMipPropertyID values set./// /// public int sourceMipPropertyID; /// /// The scalar property to set with the scale and bias also known as offset from the source to distination. If -1 the default "_BlitScaleBias" property will be used. Note: Use Shader.PropertyToID to convert a string property name to an ID. /// public int scaleBiasPropertyID; /// /// The type of full-screen geometry to use when rendering the blit material. See FullScreenGeometryType for details. /// public FullScreenGeometryType geometry; } class BlitMaterialPassData { public int sourceTexturePropertyID; public TextureHandle source; public TextureHandle destination; public Vector2 scale; public Vector2 offset; public Material material; public int shaderPass; public MaterialPropertyBlock propertyBlock; public int sourceSlice; public int destinationSlice; public int numSlices; public int sourceMip; public int destinationMip; public int numMips; public FullScreenGeometryType geometry; public int sourceSlicePropertyID; public int sourceMipPropertyID; public int scaleBiasPropertyID; } /// /// Add a render graph pass to blit an area of the source texture into the destination texture. Blitting is a high-level way to transfer texture data from a source to a destination texture. /// In this overload the data may be transformed by an arbitrary material. /// /// This function works transparently with regular textures and XR textures (which may depending on the situation be 2D array textures) if numSlices is set to -1 and the slice property works correctly. /// /// This is a helper function to schedule a simple pass calling a single blit. If you want to call a number of blits in a row (e.g. to a slice-by-slice or mip-by-mip blit) it's generally faster /// to simple schedule a single pass and then do schedule blits directly on the command buffer. /// /// This function schedules a pass for execution on the rendergraph execution timeline. It's important to ensure the passed material and material property blocks correctly account for this behavior in /// particular the following code will likely not behave as intented: /// material.SetFloat("Visibility", 0.5); /// renderGraph.AddBlitPass(... material ...); /// material.SetFloat("Visibility", 0.8); /// renderGraph.AddBlitPass(... material ...); /// /// This will result in both passes using the float value "Visibility" as when the graph is executed the value in the material is assigned 0.8. The correct way to handle such use cases is either using two separate /// materials or using two separate material property blocks. E.g. : /// /// propertyBlock1.SetFloat("Visibility", 0.5); /// renderGraph.AddBlitPass(... material, propertyBlock1, ...); /// propertyBlock2.SetFloat("Visibility", 0.8); /// renderGraph.AddBlitPass(... material, propertyBlock2, ...); /// /// Notes on using this function: /// - If you need special handling of MSAA buffers this can be implemented using the bindMS flag on the source texture and per-sample pixel shader invocation on the destination texture (e.g. using SV_SampleIndex). /// - MaterialPropertyBlocks used for this function should not contain any textures added by MaterialPropertyBlock.SetTexture(...) as it will cause untracked textures when using RenderGraph causing uninstended behaviour. /// /// /// The RenderGraph adding this pass to. /// Parameters used for rendering. /// A name to use for debugging and error logging. This name will be shown in the rendergraph debugger. /// File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it. /// File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it. public static void AddBlitPass(this RenderGraph graph, BlitMaterialParameters blitParameters, string passName = "Blit Pass Utility w. Material" #if !CORE_PACKAGE_DOCTOOLS , [CallerFilePath] string file = "", [CallerLineNumber] int line = 0) #endif { var sourceDesc = graph.GetTextureDesc(blitParameters.source); var destinationDesc = graph.GetTextureDesc(blitParameters.destination); int sourceMaxWidth = math.max(math.max(sourceDesc.width, sourceDesc.height), sourceDesc.slices); int sourceTotalMipChainLevels = (int)math.log2(sourceMaxWidth) + 1; int destinationMaxWidth = math.max(math.max(destinationDesc.width, destinationDesc.height), destinationDesc.slices); int destinationTotalMipChainLevels = (int)math.log2(destinationMaxWidth) + 1; if (blitParameters.numSlices == -1) blitParameters.numSlices = destinationDesc.slices - blitParameters.destinationSlice; if (blitParameters.numSlices > destinationDesc.slices - blitParameters.destinationSlice || (blitParameters.sourceSlice != -1 && blitParameters.numSlices > sourceDesc.slices - blitParameters.sourceSlice)) { throw new ArgumentException($"BlitPass: {passName} attempts to blit too many slices. The pass will be skipped."); } if (blitParameters.numMips == -1) blitParameters.numMips = destinationTotalMipChainLevels - blitParameters.destinationMip; if (blitParameters.numMips > destinationTotalMipChainLevels - blitParameters.destinationMip || (blitParameters.sourceMip != -1 && blitParameters.numMips > sourceTotalMipChainLevels - blitParameters.sourceMip)) { throw new ArgumentException($"BlitPass: {passName} attempts to blit too many mips. The pass will be skipped."); } using (var builder = graph.AddUnsafePass(passName, out var passData, file, line)) { passData.sourceTexturePropertyID = blitParameters.sourceTexturePropertyID; passData.source = blitParameters.source; passData.destination = blitParameters.destination; passData.scale = blitParameters.scale; passData.offset = blitParameters.offset; passData.material = blitParameters.material; passData.shaderPass = blitParameters.shaderPass; passData.propertyBlock = blitParameters.propertyBlock; passData.sourceSlice = blitParameters.sourceSlice; passData.destinationSlice = blitParameters.destinationSlice; passData.numSlices = blitParameters.numSlices; passData.sourceMip = blitParameters.sourceMip; passData.destinationMip = blitParameters.destinationMip; passData.numMips = blitParameters.numMips; passData.geometry = blitParameters.geometry; passData.sourceSlicePropertyID = blitParameters.sourceSlicePropertyID; passData.sourceMipPropertyID = blitParameters.sourceMipPropertyID; passData.scaleBiasPropertyID = blitParameters.scaleBiasPropertyID; builder.UseTexture(blitParameters.source); builder.UseTexture(blitParameters.destination, AccessFlags.Write); builder.SetRenderFunc((BlitMaterialPassData data, UnsafeGraphContext context) => BlitMaterialRenderFunc(data, context)); } } static void BlitMaterialRenderFunc(BlitMaterialPassData data, UnsafeGraphContext context) { s_BlitScaleBias.x = data.scale.x; s_BlitScaleBias.y = data.scale.y; s_BlitScaleBias.z = data.offset.x; s_BlitScaleBias.w = data.offset.y; CommandBuffer unsafeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd); if (data.propertyBlock == null) data.propertyBlock = s_PropertyBlock; data.propertyBlock.SetTexture(data.sourceTexturePropertyID, data.source); if (data.sourceSlice == -1) data.propertyBlock.SetInt(data.sourceSlicePropertyID, 0); if (data.sourceMip == -1) data.propertyBlock.SetInt(data.sourceMipPropertyID, 0); data.propertyBlock.SetVector(data.scaleBiasPropertyID, s_BlitScaleBias); for (int currSlice = 0; currSlice < data.numSlices; currSlice++) { for (int currMip = 0; currMip < data.numMips; currMip++) { if (data.sourceSlice != -1) data.propertyBlock.SetInt(data.sourceSlicePropertyID, data.sourceSlice + currSlice); if (data.sourceMip != -1) data.propertyBlock.SetInt(data.sourceMipPropertyID, data.sourceMip + currMip); context.cmd.SetRenderTarget(data.destination, data.destinationMip + currMip, CubemapFace.Unknown, data.destinationSlice + currSlice); switch (data.geometry) { case FullScreenGeometryType.Mesh: Blitter.DrawQuadMesh(unsafeCmd, data.material, data.shaderPass, data.propertyBlock); break; case FullScreenGeometryType.ProceduralQuad: Blitter.DrawQuad(unsafeCmd, data.material, data.shaderPass, data.propertyBlock); break; case FullScreenGeometryType.ProceduralTriangle: Blitter.DrawTriangle(unsafeCmd, data.material, data.shaderPass, data.propertyBlock); break; } } } } } }