标签:|| cas function metal pac 矩阵 mes ken 邮件
在 上一篇 中使用ComputeShader进行了向量和矩阵的相乘计算,然后在C#代码中通过ComputeBuffer.GetData方法从GPU中读取计算结果,这个方法是一个同步操作,即调用时会堵塞调用线程,直到GPU返回数据为止,所以在需要读取的数据量很大时会有比较高的耗时,会导致游戏卡顿影响体验。
Google了一番法线有异步的方法可以调用,在Unity2018版本以后增加了AsyncGPUReadback和AsyncGPUReadbackRequest类,可以实现异步方式从GPU读取数据,大致逻辑是:
下面是主要部分的代码
C#部分:
void Dispach()
{
   if (computeShader == null)
   {
       return;
   }
   int kernelIndex = -1;
   try
   {
       kernelIndex = computeShader.FindKernel(GetKernelName(method));
   }
   catch (Exception error)
   {
       Debug.LogFormat("Error: {0}", error.Message);
       return;
   }
   switch (method)
   {
       case EMethod.ComputerBuffer:
           if (m_comBuffer != null)
           {
               m_comBuffer.Release();
           }
           // 初始化m_dataArr //
           InitDataArr();
           m_comBuffer = new ComputeBuffer(m_dataArr.Length, sizeof(float) * Stride);
           m_comBuffer.SetData(m_dataArr);
           computeShader.SetBuffer(kernelIndex, "ResultBuffer", m_comBuffer);
           // 在Shader中只需要用到X维的数据作为数组索引,因此只需要给X维的thread group设置数值,Y维和Z维的thread group数量为1即可 //
           computeShader.Dispatch(kernelIndex, 32, 1, 1);
           break;
   }
}
void GetResultAsync()
{
   switch (method)
   {
       case EMethod.ComputerBuffer:
           if (m_comBuffer == null ||
               m_objArr == null ||
               m_dataArr == null)
           {
               break;
           }
           m_processed = false;
           m_request = AsyncGPUReadback.Request(m_comBuffer, m_dataArr.Length * Stride, 0);
           m_asyncFrameNum = 0;
           break;
   }
}
void Update()
{
    if (!m_processed)
    {
        m_asyncFrameNum++;
        if (m_request.done && !m_request.hasError)
        {
            m_processed = true;
            Profiler.BeginSample("GetDataFromGPU_Async");
            using (Timer timer = new Timer(Timer.ETimerLogType.Millisecond))
            {
                // 方式2 //
                m_request.GetData<DataStruct>(0).CopyTo(m_dataArr);
                // 方式1, ToArray 方法会有GC产生 //
                //m_dataArr = null;
                //m_dataArr = m_request.GetData<DataStruct>(0).ToArray();
            }
            Profiler.EndSample();
            if (m_computeShaderWarmedUp)
            {
                Callback();
            }
            else
            {
                m_computeShaderWarmedUp = true;
            }
            Scene curScene = SceneManager.GetActiveScene();