C# XNA 低帧率

6

好的,我有80,000个带有简单纹理的“盒子”网格, 我已经设置了视距, 只绘制您可以看到的部分, 这为下面的DrawModel函数留下了600到1000个网格。 问题是,我只能得到每秒10帧,而我的视距也很糟糕。 此外,我对所有代码进行了内存测试,“mesh.draw()”会使帧率降低30帧, 没有其他东西接近那么多。 需要帮助吗?

        private void DrawModel(MeshHolder tmpMH)
        {          
            Model tmpDrawModel = (Model)_Meshs[tmpMH.MeshFileName];
            Matrix[] transforms = new Matrix[tmpDrawModel.Bones.Count];
            tmpDrawModel.CopyAbsoluteBoneTransformsTo(transforms);
            foreach (ModelMesh mesh in tmpDrawModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {

                    effect.LightingEnabled = false;

                    effect.TextureEnabled = true;
                    effect.Texture = (Texture2D)_Textures[tmpMH.GetTexture(Count)]; 



                    effect.View = _MainCam.View;
                    effect.Projection = _projection;
                    effect.World =
                         transforms[mesh.ParentBone.Index] *
                        Matrix.CreateFromYawPitchRoll(tmpMH.Rotation.Y, tmpMH.Rotation.X, tmpMH.Rotation.Z) *
                        Matrix.CreateScale(tmpMH.Scale) *
                        Matrix.CreateTranslation(tmpMH.Position);
                }

                    mesh.Draw();               
            }
        }

这与这个问题非常相似。 - Andrew Russell
2个回答

7
“你所说的影响性能的问题是ModelMesh.Draw。当你绘制模型时,它的工作方式如下:”
for each frame
  for each Model
    for each ModelMesh // you call Draw(), which does:
      for each ModelMeshPart
        for each Effect
          for each EffectPass
            Draw some triangles // sends a batch of instructions to the GPU

所以问题是:每帧您发送多少批次到GPU?因为在饱和CPU之前,您每帧只能发送几千个*批次-达到“批次限制”。(每个批次在图形驱动程序中使用CPU时间-它还使用一些带宽和GPU时间,但CPU时间占主导地位。)
您可能想阅读this answerthis answer以及this slide deck获取更多信息。
解决方案是修改场景(例如:合并一些网格部分,进行一些裁剪,添加实例支持)以减少您发送到GPU的批次数。
此外,在您的DrawUpdate循环中,尽量避免使用这样的东西:
Matrix[] transforms = new Matrix[tmpDrawModel.Bones.Count];

您应该尽力避免每帧发生的内存分配 - 因为它们最终会导致昂贵的垃圾回收,可能会导致帧速率的中断(特别是在Xbox上)。尝试将缓冲区存储在某个地方并重复使用。


1
BatchBatchBatch.pdf的链接似乎现在无法使用。这是一个可用的链接:http://origin-developer.nvidia.com/docs/IO/8230/BatchBatchBatch.pdf?q=docs/IO/8230/BatchBatchBatch.pdf - Venesectrix
那个也坏了,现在我已经更新到一个可用的更新版本。 - Andrew Russell

3
effect.World =
    transforms[mesh.ParentBone.Index] *
    Matrix.CreateFromYawPitchRoll(
      tmpMH.Rotation.Y, tmpMH.Rotation.X, tmpMH.Rotation.Z) *
    Matrix.CreateScale(tmpMH.Scale) *
    Matrix.CreateTranslation(tmpMH.Position);

虽然我不是一名分析师,但我觉得这行代码很痛苦。矩阵的创建和乘法运算非常耗费资源!我知道这段代码是必要的,所以除非你能预先计算这些矩阵,否则我建议你尝试以下方法:

Matrix pitch, scale, translation, temp1, temp2;

Matrix.CreateFromYawPitchRoll(
    tmpMH.Rotation.Y, tmpMH.Rotation.X, tmpMH.Rotation.Z, out pitch);
Matrix.CreateScale(ref tmpMH.Scale, out scale);
Matrix.CreateTranslation(ref tmpMH.Position, out translation);
Matrix.Multiply(ref transforms[mesh.ParentBone.Index], ref pitch, out temp1);
Matrix.Multiply(ref temp1, ref scale, out temp2);
Matrix.Multiply(ref temp2, ref translation, out effect.World);

这可能会更快,因为没有必要为参数传递在堆栈上复制每个矩阵(需要复制的东西少了20多倍!)


1
真的怀疑未经微调的矩阵计算在这里占据了CPU使用率的主导地位。此外,effect.World不能是一个out参数。 - Andrew Russell
在我看来,这会有所帮助。复制东西的代价相当高,不是指CPU使用率,而是因为它会减慢一切速度,因为CPU需要等待内存(http://en.wikipedia.org/wiki/Wait_state)。 - BlackBear
无论使用哪个版本的代码,你都不会触及主内存。这里的堆栈数据量很小,可以轻松地适应CPU缓存。而将CPU置于等待状态(例如由于缓存未命中)仍然算作对CPU限制的“使用”。你的代码会“有所帮助”,因为它需要更少的指令 - 但只是微乎其微的数量 - 肯定不足以使OP低于CPU限制。 - Andrew Russell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接