写入RenderTarget后,如何有效地克隆输出?

9 c# xna texture2d render-to-texture

XNA noob在这里,每天都在学习.我刚刚研究了如何使用RenderTarget2D将多个纹理合成为一个.然而,虽然我可以将RenderTarget2D用作Texture2D用于大多数目的,但是有一个关键的区别:当后缓冲区调整大小时这些渲染的纹理会丢失(毫无疑问,在其他情况下,例如内存不足的图形设备).

目前,我只是将完成的RenderTarget2D复制到一个新的非易失性Texture2D对象中.不过,我这样做的代码非常难看.有没有更优雅的方式来做到这一点?也许我只是累了,但我无法在Google或SO上找到答案.

略有简化:

public static Texture2D  MergeTextures(int width, int height, IEnumerable<Tuple<Texture2D, Color>> textures)
    {
    RenderTarget2D  buffer = new RenderTarget2D(_device, width, height);

    _device.SetRenderTarget(buffer);
    _device.Clear(Color.Transparent);

    SpriteBatch  spriteBatch = new SpriteBatch(_device);
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);

    // Paint each texture over the one before, in the appropriate color
    Rectangle  rectangle = new Rectangle(0, 0, width, height);
    foreach (Tuple<Texture2D, Color> texture in textures)
        spriteBatch.Draw(texture.Item1, rectangle, texture.Item2);

    spriteBatch.End();
    _device.SetRenderTarget((RenderTarget2D)null);

    // Write the merged texture to a Texture2D, so we don't lose it when resizing the back buffer
    // This is POWERFUL ugly code, and probably terribly, terribly slow
    Texture2D  mergedTexture = new Texture2D(_device, width, height);
    Color[]    content       = new Color[width * height];
    buffer.GetData<Color>(content);
    mergedTexture.SetData<Color>(content);
    return mergedTexture;
    }
Run Code Online (Sandbox Code Playgroud)

我想我应该检查IsContentLost并根据需要重新渲染,但这发生在我的主绘图循环中间,当然你不能嵌套SpriteBatches.我可以维护一个"渲染TODO"列表,在主SpriteBatch结束后处理那些,然后它们可用于下一帧.这是首选策略吗?

这个代码只被调用了几次,因此性能不是问题,但我想学习如何做正确的事情.

And*_*ell 4

实际上,如果您在正常加载内容(游戏开始、关卡更改、房间更改等)时在一次性过程中生成纹理,那么您的代码并没有那么糟糕。您正在 CPU 和 GPU 之间传输纹理,这与加载普通纹理相同。这很简单而且有效!

如果您更频繁地生成纹理,并且它开始成为每帧成本,而不是加载时间成本,那么您将需要担心其性能,并可能将它们保留为渲染目标。

您不应该参与ContentLost绘图,因此您可以安全地响应该事件并重新创建渲染目标。或者您可以检查IsContentLost它们中的每一个,最好是在渲染其他任何内容之前在帧开始时检查。无论哪种方式,在开始之前都应该检查一切SpriteBatch

(通常,当使用渲染目标时,无论如何,您都会在每一帧重新生成它们,因此在这种情况下您不需要检查它们。)