https://blog.csdn.net/h5502637/article/details/85637872

2021-03-02 23:26

阅读:693

标签:||   cas   function   metal   pac   矩阵   mes   ken   邮件   

 

在 上一篇 中使用ComputeShader进行了向量和矩阵的相乘计算,然后在C#代码中通过ComputeBuffer.GetData方法从GPU中读取计算结果,这个方法是一个同步操作,即调用时会堵塞调用线程,直到GPU返回数据为止,所以在需要读取的数据量很大时会有比较高的耗时,会导致游戏卡顿影响体验。
Google了一番法线有异步的方法可以调用,在Unity2018版本以后增加了AsyncGPUReadbackAsyncGPUReadbackRequest类,可以实现异步方式从GPU读取数据,大致逻辑是:

  1. AsyncGPUReadback.Request 发起一个异步获取数据的请求,返回一个AsyncGPUReadbackRequest对象
  2. 在Update中每帧检测该异步请求是否完成,完成的话就去该请求对象中获取数据

下面是主要部分的代码
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.GetDataDataStruct>(0).CopyTo(m_dataArr);

                // 方式1, ToArray 方法会有GC产生 //
                //m_dataArr = null;
                //m_dataArr = m_request.GetData(0).ToArray();
            }

            Profiler.EndSample();

            if (m_computeShaderWarmedUp)
            {
                Callback();
            }
            else
            {
                m_computeShaderWarmedUp = true;
            }

            Scene curScene = SceneManager.GetActiveScene();
            


评论


亲,登录后才可以留言!