• 使用Nsight查找CE3的渲染bug


    工作临时的接的一个小任务,查找ce3引擎修改后在绘制上出的一点bug
    在代码的底层调用代码做了一些修改后,场景里的绘制的问题,因为也是刚接触CE3代码,也只能通过Nsight来查找问题了。
     
    首先用Nsight对正确和错误两个版本做次对比
    下图是正确的版本
    这个是错误版本
    对比后,先是定位出问题绘制事件
     
    定位到应该是FOG绘制的问题
     
    再看一下当前API设置的Pixel Shader的Source code
    pixout FogPassPS(vert2fragFog IN)
    {
        pixout OUT;
        float sceneDepth;
        half4 localFogColor;
        float3 worldPos,cameraToWorldPos;
        FogPassCommon(IN,sceneDepth,localFogColor,worldPos,cameraToWorldPos);
        localFogColor.a=1.0-localFogColor.a;
        localFogColor.xyz*=HDRParams2.y;
        HDROutput(OUT,localFogColor,1);
        return OUT;
    }

    从shader的名字看,确实是FogPass的问题,先把代码回滚回正确的版本后,对两个版本fogpass的shader代码做比对,没有任何区别,排除了shader source code 出错的可能性。

    然后再对两个版本的IS, VS, XFORM/RS, PS, OT逐一比较
    正确版本的Output
    错误版本的Output
    发现了SrcBlendAlpha的设置错误,绘制fog时的AlphaBelnd设置也出了问题,另外Stencil的输出也不对,不过因为这个draw event的时候,SeperateAlphaBlendEnable和StencilEnable都是FALSE,所以画面绘制问题并不是这个造成的,不过还是需要记录下来以后进行修改。
    然后,这时候发现绘制fog时,Ps时时使用的Texture Sample,两个版本也有区别
    一个是SceneDiffuseAcc,也就是deferred light的漫反射累积光照的问题


    另外就是在,ZPass阶段的ZMap的深度输出信息,ZTarget也不一样,因为ZTarget的输出在更前面,那么就先解决它的问题
     
    正确版本的ZTraget
    错误版本的ZTarget
    从上图可以看到SceneNormalsMap和ZTarget的信息是不一样的,而且错误版本好像是这两个rt输出的信息互换了,SceneNormalsMap的RT格式是A8R8G8B8,
    ZTarget的RT格式是R32F。
     
    为验证这个猜测,跟踪到计算ZTarget的Draw Event附近,对比一下上下文的RT的输出差别
    正确版本
     
    错误版本
    可以确定了,最后gbuffer的RT_0输出的到的目标surface错误的,本应该是A8R8G8B8格式的SceneNormalsMap,变成了R32F格式的ZTarget。
    定位到错误后,就是到代码里去查找BUG了=v=。
     
    一开始的想法比较简单,认为应该就是renderTarget的目标被错误设置的问题,那么,先根据Perf Marker信息,定位到处理ZPASS阶段的函数
    1. PROFILE_LABEL_PUSH("ZPASS");
      FX_ProcessZPassRenderLists();
      PROFILE_LABEL_POP("ZPASS");

      CD3D9Renderer::FX_ProcessZPassRenderLists,而它通过调用CD3D9Renderer::FX_ZScene,来设置rendertarget

    1. if( bRenderNormalsOnly )
      {
          FX_PushRenderTarget(0,CTexture::s_ptexSceneNormalsMap,&m_DepthBufferOrigMSAA,false,-1,true);
      }
      else
      {
          FX_PushRenderTarget(0,CTexture::s_ptexZTarget,&m_DepthBufferOrigMSAA,false,-1,true);
          FX_PushRenderTarget(1,CTexture::s_ptexSceneNormalsMap, NULL);
      }

      这里FX_PushRenderTarget并不会立即调用d3d的api设置rendertarget,而是把信息保存在RT的stack里,等commit时再统一进行设置,条件语句if成功的分支,就是把ZTarget设置到RT0的阶段,但从

    但通过runtime时debug代码来看,这部分的指针传递都是没问题的。
    上层API没有问题,那么就只能从底层d3ddevice的SetRenderTarget来查找问题了
    1. if(NewRenderTargets== NULL ||!RTs[i]->GetRenderTargetView())
      {
          mNativeD3D9Device->SetRenderTarget(i, NULL);
      }
      else
      {
          IDirect3DSurface9* pSurf =(IDirect3DSurface9*)RTs[i]->GetRenderTargetView()->GetNativeResource();
          mNativeD3D9Device->SetRenderTarget( i, pSurf );
      }

      上面是同事修改过后的SetRednerTarget函数,在这里设置断点,结果发现传递进去的pSurf指针也是正确的,那么说明,这部分API的调用是没问题的,很有可能是SetRenderTarget失败,导致前一个pass里,设置的RT0 surface,也就是R32F ZTarget被保留到了下一个pass里继续使用了。

    重新单步调试,遍历这部分流程,发现调用CD3D9Renderer::FX_PopRenderTarget()函数的时候,也就是把当前的RT保存到texutre的操作,也会有SetRednerTarget的操作。恰好也就在前面切换RT0的绑定surface之间调用的。也就是说,如果我们已经把SceneNormalsMap设置到RT1后,如果不把RT1清空,就立即把它设置到RT0,那么这部操作是不会成功的。
     
    也就是
    1. D3D9Device->SetRenderTarget(0,ZTarget);
      D3D9Device->SetRenderTarget(1,SceneNormal);
      
      D3D9Device->SetRenderTarget(0,SceneNormal);//错误
      D3D9Device->SetRenderTarget(0,ZTarget);
      D3D9Device->SetRenderTarget(1,SceneNormal);
      
      D3D9Device->SetRenderTarget(1,0);
      D3D9Device->SetRenderTarget(0,SceneNormal);//正确

      而我们修改过的代码,因为某些逻辑问题,设置RT1为NULL的操作被跳过了

    打开Nsight的Event list
    可以看到正确版本的Eevnt99部分有设置RT1为NULL的操作,而如果跳过这部操作的话,Event135的的设置会返回错误代码。也就是之前的ZTarget会被继续设置在RT0上。
    同事修改了代码后,这部分总算是正确了=0=。
    在查找bug过程中中,还发现了绘制纹理的亮度有差别
    正确版本
    错误版本
    排查后在读取贴图的地方发现,是SRGBTexture设置的问题。

    CE老版本的设置方法,而DX11风格变成了统一设置,而我们把这个参数忘记了。
    1. LPDIRECT3DDEVICE9 dv = gcpRendD3D->GetD3DDevice();
      bool sRGBRead = gRenDev->IsLinearSpaceShadingEnabled()&&( m_bIsSRGB || s_TexStates[nTS].m_bSRGBLookup &&(m_nFlags& FT_USAGE_ALLOWREADSRGB));
      STexState*pDTS =&TexStages[nTUnit].m_State;
      if(pDTS->m_bSRGBLookup != sRGBRead )
      {
          pDTS->m_bSRGBLookup = sRGBRead;
          dv->SetSamplerState(nTUnit + nVtxTexOffSet, D3DSAMP_SRGBTEXTURE, sRGBRead ? TRUE : FALSE);
      }

      然后,又回到了SceneDiffuseAcc渲染错误的问题上了。这个问题略坑,还没找到正确解决方法,
      具体现象是,在正确版本里,把法线,UV,以及其他定值作为output输出

    Normal
    UV.x
    可以看到上面部分都被clip掉了,而查看了所以可能的状态,设置都为false
    而我们的新版本,不设置这些状态,上面部分也要绘制,所以才会出现ScenediffuseAcc错误信息
    错误的版本,红框部分因为深度过大,本来是不应该绘制的。
    解决办法,只有暂时在shader里添加一个clip函数来把深度过大的地方去掉。
     
    11月7日添加: 最后发现是同事把nv的depthboundTest实现的代码注释掉了,也难怪nsight和pix都差不到= =
  • 相关阅读:
    Jar包管理规范
    Base64编码原理与应用
    MySQL 5.7.14安装说明,解决服务无法启动
    idea注册
    Oracle 如何对中文字段进行排序
    SVN错误:Attempted to lock an already-locked dir
    排序算法
    设计模式
    分层
    阿里云
  • 原文地址:https://www.cnblogs.com/TracePlus/p/4079816.html
Copyright © 2020-2023  润新知