【问题标题】:Is there an inexpensive way to transfer colour data from a RenderTarget2D on a per frame basis?是否有一种廉价的方法可以按帧从 RenderTarget2D 传输颜色数据?
【发布时间】:2012-03-07 15:54:47
【问题描述】:

直到最近,我们的游戏通过从场景背景纹理的一部分中获取颜色数据来检查碰撞。这工作得很好,但是随着设计的改变,我们需要检查多个纹理,因此决定将这些全部渲染到单个 RenderTarget2D 并检查其上的碰撞。

public bool TouchingBlackPixel(GameObject p)
    {
        /*
        Calculate rectangle under the player...
        SourceX,SourceY: Position of top left corner of rectangle
        SizeX,SizeY: Aproximated (cast to int from float) size of box
        */

        Rectangle sourceRectangle = new Rectangle(sourceX, sourceY,
                                                   (int)sizeX, (int)sizeY);

        Color[] retrievedColor = new Color[(int)(sizeX * sizeY)];

        p.tileCurrentlyOn.collisionLayer.GetData(0, sourceRectangle, retrievedColor,
                                                     0, retrievedColor.Count());

        /*
        Check collisions
        */
    }

我们遇到的问题是,自从移动到渲染目标后,我们的 FPS 大幅下降。

从我们读到的内容看来,问题似乎是为了从 RenderTarget2D 获取数据,您需要将数据从 GPU 传输到 CPU,而且速度很慢。由于我们需要运行相同的函数两次(每个玩家一次)并且无法保留相同的数据(它们可能不在同一个图块上),这使情况更加复杂。

我们已经尝试将 GetData 调用移动到磁贴的 Draw 函数并将数据存储在成员数组中,但这似乎并没有解决问题(因为我们仍然经常在磁贴上调用 GetData - 从每次更新两次到每次抽奖一次)。

您能给我们的任何帮助都会非常棒,因为这个碰撞系统为我们提供的功能非常棒,但是渲染目标引入的开销将使其无法保留。

【问题讨论】:

  • 您是否在着色器中生成碰撞层?通常,您会在 CPU 上保留一个本地副本,并使用该副本或派生数据结构进行碰撞检测,或者在着色器中处理碰撞。
  • 碰撞层是通过在图形设备上设置渲染目标然后绘制一个精灵批次来创建的。我不认为这使用着色器。

标签: c# windows xna xna-4.0


【解决方案1】:

简单的答案是:不要那样做

听起来,将碰撞数据的合成卸载到 GPU 是一项不起作用的性能优化 - 所以正确的做法是恢复更改。

您应该简单地在 CPU 上进行碰撞检查。而且我会进一步建议,多次运行碰撞算法并通过组合结果来确定碰撞响应可能更快,而不是将整个场景合成到单个图层并运行一次碰撞检测。

如果您在进行碰撞之前使用渲染目标来支持转换,情况尤其如此。

(对于简单的 2D 像素碰撞检测,请参阅 this sample。如果您需要对转换的支持,请参阅 this sample。)

【讨论】:

  • 将其移至 GPU 并不是我们的选择,因为我们想一次检查所有层并且渲染目标存在于 GPU 上,但我了解您的意思。这是否意味着没有不利的帧率就无法使用我们当前的系统?将碰撞检测本身卸载到 GPU 以避免对数据进行洗牌是个好主意吗?我之前看过这些例子,我们的系统实际上比它们好一点,但是当你使用渲染目标时(就像他们的一样),由于需要从 GPU 到 CPU 的传输,它就会崩溃。
  • 我不能肯定地说有 no 方法可以在某些帧速率/硬件预算中获得它(特别是因为我没有详细信息)。但我可以向你保证,这是一个非常非常缓慢的操作。你最好把编程和硬件资源花在 CPU 上。如果您在任何时候都必须将数据带回 CPU,那么将整个事情卸载到 GPU 上可能是一个糟糕的计划。
  • 我认为你可能是对的。我们只需要分别检查每个对象上的碰撞,而不是统一系统。很遗憾,因为我们有一个很好的系统,它允许从碰撞中获得一些相当令人印象深刻的反馈,但是每帧访问 gpu 的开销太高了。
  • @LlamaCloud 如果您的系统需要合成场景,这仍然可以在 CPU 上进行。这不是推荐的方式 - 因为性能 - 但如果一次与整个场景碰撞有一些切实的好处,你应该去。不过,您需要做很多优化。可能想查找一些资源的“blitting”。您将特别想要做的是确保您只处理碰撞数据 - 无需重新创建全彩色场景。为每个精灵预先创建黑白碰撞蒙版并使用它们。
  • 谢谢,我会调查的。系统根据碰撞像素的颜色数据确定行为,所以我不太热衷于去除颜色,但这可能会以其他方式进行编码。一位同事还建议,在绘制完整场景后绘制到较小的渲染目标(受限于玩家的大小和位置)可能会保留原始系统,因为 GPU 不会处理那么多的数据传输但我还没有尝试过。您认为这可能是一个有效的建议吗?
【解决方案2】:

我想,你的瓷砖的碰撞层没有改变。或者至少变化不是很频繁。因此,您可以将每个图块的颜色存储在数组或其他结构中。这会减少从 GPU 传输到 CPU 的数据量,但要求存储在 RAM 中的额外数据不能太大。

【讨论】:

  • 这样没问题,但我们不能排除碰撞层会改变的可能性。我们目前有一个具有不断变化的层的图块,并且它们都可以由玩家赋予它们变化。这确保了使用这种方法是不可能的,因为一旦玩家进入一个移动的瓦片(或自己改变瓦片),我们就必须获取新数据,这会立即杀死帧率。
  • 另一种选择是实现一种缓存系统,该系统仅在 GPU 已更新且必要时才轮询来自 GPU 的信息。
  • 正如问题文本中提到的(不过,我注意到,有点不准确,我会进行编辑以纠正),我们尝试在数据绘制时抓取数据,但这并没有产生明显改善。我们没有尝试确保它只在发生变化时才抓取它,但我们必须能够使用这个系统,即使它每帧都发生变化(就像在我们的“雨”场景中所做的那样),所以这不能解决问题。
猜你喜欢
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-04
  • 2010-12-22
  • 1970-01-01
相关资源
最近更新 更多