您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

228 行
10 KiB

using System;
using System.Collections.Generic;
using System.Text;
namespace UnityEngine.Experimental.Rendering
{
/// <summary>
/// Declares what should be generated in utility code.
/// It will generate a compute shader and a C# class to use the compute shader with a ComputeBuffer
///
/// Exemple:
/// - I add a CopyOperation { sourceChannel = 4, targetChannel = 2, subscript = "zx" }
/// => a Kernel will be generated to copy from a TextureRGBA the AR channels into a TextureRG
/// => A method will be added to call the kernel in the C# class GPUCopy (SampleCopy_xyzw2zx)
///
/// C# Exemple:
/// // Initialize the gpucopy
/// var gpuCopy = new CPUCopy(generatedComputeShaderAsset);
///
/// CommandBuffer cmb = ...
/// gpuCopy.SampleCopyChannel_xyzw2x(cmb, _SourceTexture, _TargetTexture, new Vector2(targetWidth, targetHeight));
///
/// Initialization:
/// - You must set the generated ComputeShader as argument of the constructor of the generated GPUCopy C# class
/// </summary>
public class GPUCopyAsset : ScriptableObject
{
static string[] k_ChannelIDS = { "x", "xy", "xyz", "xyzw" };
const int k_KernelSize = 8;
[Serializable]
public struct CopyOperation
{
public string subscript;
public int sourceChannel;
public int targetChannel;
}
[SerializeField]
CopyOperation[] m_CopyOperation = new CopyOperation[0];
public void Generate(out string computeShader, out string csharp)
{
var operations = m_CopyOperation;
var sources = new HashSet<int>();
var targets = new HashSet<int>();
var cc = new StringBuilder(); // Compute Shader out
var ccp = new StringBuilder(); // Compute properties
var cck = new StringBuilder(); // Compute kernel
var cs = new StringBuilder(); // CSharp out
var csm = new StringBuilder(); // CSharp methods
var csc = new StringBuilder(); // CSharp constructor
var csp = new StringBuilder(); // CSharp properties
for (var i = 0; i < operations.Length; i++)
{
var o = operations[i];
sources.Add(o.sourceChannel);
targets.Add(o.targetChannel);
}
ccp.AppendLine();
ccp.AppendLine("CBUFFER_START (UnityCBuffer)");
ccp.AppendLine(" uint2 _RectOffset;");
ccp.AppendLine("CBUFFER_END");
ccp.AppendLine();
csm.AppendLine(" static readonly int _RectOffset = Shader.PropertyToID(\"_RectOffset\");");
foreach (var target in targets)
{
ccp.AppendLine(string.Format("RWTexture2D<float{0}> _Result{0};", target.ToString()));
csm.AppendLine(string.Format(" static readonly int _Result{0} = Shader.PropertyToID(\"_Result{0}\");", target.ToString()));
}
foreach (var source in sources)
{
ccp.AppendLine(string.Format("Texture2D<float{0}> _Source{0};", source.ToString()));
csm.AppendLine(string.Format(" static readonly int _Source{0} = Shader.PropertyToID(\"_Source{0}\");", source.ToString()));
}
csm.AppendLine(@" void SampleCopyChannel(
CommandBuffer cmd,
RectInt rect,
int _source,
RenderTargetIdentifier source,
int _target,
RenderTargetIdentifier target,
int kernel8,
int kernel1)
{
RectInt main, topRow, rightCol, topRight;
unsafe
{
RectInt* dispatch1Rects = stackalloc RectInt[3];
int dispatch1RectCount = 0;
RectInt dispatch8Rect = RectInt.zero;
if (TileLayoutUtils.TryLayoutByTiles(
rect,
8,
out main,
out topRow,
out rightCol,
out topRight))
{
if (topRow.width > 0 && topRow.height > 0)
{
dispatch1Rects[dispatch1RectCount] = topRow;
++dispatch1RectCount;
}
if (rightCol.width > 0 && rightCol.height > 0)
{
dispatch1Rects[dispatch1RectCount] = rightCol;
++dispatch1RectCount;
}
if (topRight.width > 0 && topRight.height > 0)
{
dispatch1Rects[dispatch1RectCount] = topRight;
++dispatch1RectCount;
}
dispatch8Rect = main;
}
else if (rect.width > 0 && rect.height > 0)
{
dispatch1Rects[dispatch1RectCount] = rect;
++dispatch1RectCount;
}
cmd.SetComputeTextureParam(m_Shader, kernel8, _source, source);
cmd.SetComputeTextureParam(m_Shader, kernel1, _source, source);
cmd.SetComputeTextureParam(m_Shader, kernel8, _target, target);
cmd.SetComputeTextureParam(m_Shader, kernel1, _target, target);
if (dispatch8Rect.width > 0 && dispatch8Rect.height > 0)
{
var r = dispatch8Rect;
cmd.SetComputeIntParams(m_Shader, _RectOffset, (int)r.x, (int)r.y);
cmd.DispatchCompute(m_Shader, kernel8, (int)Mathf.Max(r.width / 8, 1), (int)Mathf.Max(r.height / 8, 1), 1);
}
for (int i = 0, c = dispatch1RectCount; i < c; ++i)
{
var r = dispatch1Rects[i];
cmd.SetComputeIntParams(m_Shader, _RectOffset, (int)r.x, (int)r.y);
cmd.DispatchCompute(m_Shader, kernel1, (int)Mathf.Max(r.width, 1), (int)Mathf.Max(r.height, 1), 1);
}
}
}");
csc.AppendLine(" public GPUCopy(ComputeShader shader)");
csc.AppendLine(" {");
csc.AppendLine(" m_Shader = shader;");
for (var i = 0; i < operations.Length; i++)
{
var o = operations[i];
// Compute kernel
var kernelName8 = string.Format("KSampleCopy{0}_{1}_{2}_8", o.sourceChannel.ToString(), o.targetChannel.ToString(), o.subscript);
var kernelName1 = string.Format("KSampleCopy{0}_{1}_{2}_1", o.sourceChannel.ToString(), o.targetChannel.ToString(), o.subscript);
cck.AppendLine(string.Format("#pragma kernel {0} KERNEL_NAME={0} KERNEL_SIZE=8", kernelName8));
cck.AppendLine(string.Format("#pragma kernel {0} KERNEL_NAME={0} KERNEL_SIZE=1", kernelName1));
cck.AppendLine(@"[numthreads(KERNEL_SIZE, KERNEL_SIZE, 1)]");
cck.AppendLine(@"void KERNEL_NAME(uint2 dispatchThreadId : SV_DispatchThreadID)");
cck.AppendLine("{");
cck.AppendLine(string.Format(" _Result{0}[_RectOffset + dispatchThreadId] = LOAD_TEXTURE2D(_Source{1}, _RectOffset + dispatchThreadId).{2};",
o.targetChannel.ToString(), o.sourceChannel.ToString(), o.subscript));
cck.AppendLine("}");
cck.AppendLine();
// CSharp kernel index
var channelName = k_ChannelIDS[o.sourceChannel - 1];
var kernelIndexName8 = string.Format("k_SampleKernel_{0}2{1}_8", channelName, o.subscript);
var kernelIndexName1 = string.Format("k_SampleKernel_{0}2{1}_1", channelName, o.subscript);
csp.AppendLine(string.Format(" int {0};", kernelIndexName8));
csp.AppendLine(string.Format(" int {0};", kernelIndexName1));
// CSharp constructor
csc.AppendLine(string.Format(" {0} = m_Shader.FindKernel(\"{1}\");", kernelIndexName8, kernelName8));
csc.AppendLine(string.Format(" {0} = m_Shader.FindKernel(\"{1}\");", kernelIndexName1, kernelName1));
// CSharp method
csm.AppendLine(string.Format(@" public void SampleCopyChannel_{0}2{1}(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier target, RectInt rect)", channelName, o.subscript));
csm.AppendLine(" {");
csm.AppendLine(string.Format(" SampleCopyChannel(cmd, rect, _Source{0}, source, _Result{1}, target, {2}, {3});", o.sourceChannel.ToString(), o.targetChannel.ToString(), kernelIndexName8, kernelIndexName1));
csm.AppendLine(" }");
}
csc.AppendLine(" }");
// Compute Shader
cc.AppendLine(@"// Autogenerated file. Do not edit by hand");
cc.AppendLine();
cc.AppendLine("#pragma only_renderers d3d11 ps4 xboxone vulkan metal");
cc.AppendLine(@"#include ""../ShaderLibrary/Common.hlsl""");
cc.AppendLine(ccp.ToString()); // Properties
cc.AppendLine(cck.ToString()); // Kernels
// CSharp
cs.AppendLine(@"// Autogenerated file. Do not edit by hand");
cs.AppendLine(@"using System;");
cs.AppendLine(@"using UnityEngine.Rendering;");
cs.AppendLine();
cs.AppendLine(@"namespace UnityEngine.Experimental.Rendering");
cs.AppendLine("{");
cs.AppendLine(" public class GPUCopy");
cs.AppendLine(" {");
cs.AppendLine(" ComputeShader m_Shader;");
cs.AppendLine(csp.ToString()); // Properties
cs.AppendLine(csc.ToString()); // Constructor
cs.AppendLine(csm.ToString()); // methods
cs.AppendLine(" }");
cs.AppendLine("}");
computeShader = cc.ToString();
csharp = cs.ToString();
}
void OnValidate()
{
for (var i = 0; i < m_CopyOperation.Length; i++)
{
var o = m_CopyOperation[i];
o.sourceChannel = Mathf.Clamp(o.sourceChannel, 1, k_ChannelIDS.Length);
o.targetChannel = Mathf.Clamp(o.targetChannel, 1, k_ChannelIDS.Length);
m_CopyOperation[i] = o;
}
}
}
}