C# 写入RenderTarget后,如何高效地克隆输出?

C# 写入RenderTarget后,如何高效地克隆输出?,c#,xna,texture2d,render-to-texture,C#,Xna,Texture2d,Render To Texture,XNA noob在这里,每天学习。我刚刚研究了如何使用RenderTarget2D将多个纹理合成为一个纹理。但是,尽管我可以在大多数情况下将RenderTarget2D用作纹理2D,但有一个关键区别:当调整backbuffer的大小时(毫无疑问,在其他情况下,如内存不足的图形设备),这些渲染纹理将丢失 目前,我只是将完成的RenderTarget2D复制到一个新的非易失性Texture2D对象中。不过,我的代码非常复杂。有没有比这更优雅的方法?也许我只是累了,但我在谷歌上找不到答案 稍微简化:

XNA noob在这里,每天学习。我刚刚研究了如何使用RenderTarget2D将多个纹理合成为一个纹理。但是,尽管我可以在大多数情况下将RenderTarget2D用作纹理2D,但有一个关键区别:当调整backbuffer的大小时(毫无疑问,在其他情况下,如内存不足的图形设备),这些渲染纹理将丢失

目前,我只是将完成的RenderTarget2D复制到一个新的非易失性Texture2D对象中。不过,我的代码非常复杂。有没有比这更优雅的方法?也许我只是累了,但我在谷歌上找不到答案

稍微简化:

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;
    }
公共静态纹理2D合并纹理(int-width、int-height、IEnumerable纹理)
{
RenderTarget2D缓冲区=新的RenderTarget2D(_设备、宽度、高度);
_device.SetRenderTarget(缓冲区);
_设备。清晰(彩色。透明);
SpriteBatch SpriteBatch=新SpriteBatch(_设备);
spriteBatch.Begin(SpriteSortMode.Immediate、BlendState.NonPremultiplied);
//在前一个纹理上以适当的颜色绘制每个纹理
矩形=新矩形(0,0,宽度,高度);
foreach(纹理中的元组纹理)
绘制(texture.Item1,矩形,texture.Item2);
spriteBatch.End();
_device.SetRenderTarget((RenderTarget2D)null);
//将合并的纹理写入Texture2D,以便在调整后缓冲区大小时不会丢失它
//这是一个强大而丑陋的代码,而且可能非常非常缓慢
Texture2D mergedTexture=新的Texture2D(_设备、宽度、高度);
颜色[]内容=新颜色[宽度*高度];
获取数据(内容);
mergedTexture.SetData(内容);
返回合并文本;
}

我想我应该检查IsContentLost并根据需要重新渲染,但是这发生在我的主要绘图循环中间,当然你不能嵌套分批。我可以维护一个“渲染待办事项”列表,在主SpriteBatch结束后处理它们,然后它们就可以用于下一帧。这是首选策略吗


这段代码只调用了几次,所以性能不是一个问题,但我想学习如何正确处理。

事实上,如果您在正常加载内容(游戏开始、级别更改、房间更改等)时,在一次性过程中生成纹理,那么您的代码并没有那么糟糕。你们在CPU和GPU之间传输纹理,和加载普通的ol纹理一样。它很简单而且很有效

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

你不应该在绘图中间获得<代码>内容丢失<代码>,这样你就可以安全地响应这个事件,然后重新创建渲染目标。或者,您可以在每一帧上检查

IsContentLost
,最好是在渲染其他内容之前在帧的开头。无论哪种方式,都应该在
SpriteBatch
开始之前检查所有内容


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

如果在其他地方绘制渲染目标时遇到动态调整渲染目标大小的问题,您可以使用设置大小的屏幕外rendertarget,将完成的RT复制为如下所示:

Rendertarget2D offscreenRT = new RenderTarget2D(_device, width, height);
_device.SetRenderTarget(offscreenRT);
_device.Clear(Color.Transparent);

SpriteBatch  spriteBatch = new SpriteBatch(_device);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
spriteBatch.Draw(buffer, Vector2.Zero, Color.White);
spriteBatch.End();
_device.SetRenderTarget(null);
替换

Texture2D  mergedTexture = new Texture2D(_device, width, height);
Color[]    content       = new Color[width * height];
buffer.GetData<Color>(content);
mergedTexture.SetData<Color>(content);
return mergedTexture;
因为RenderTarget2D扩展了Texture2D,所以只会返回Texture2D类数据。另外,如果您感兴趣,这里有一个我为用多种纹理构建GUI库的小部件而设计的类。以防你需要经常做这种事情

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;

namespace Voodo.Utils {

    /// <summary>
    /// 
    /// </summary>
    public class TextureBaker {

        private readonly SpriteBatch _batch;
        private readonly RenderTarget2D _renderTarget;
        private readonly GraphicsDevice _graphicsDevice;

        /// <summary>
        /// 
        /// </summary>
        public Rectangle Bounds {
            get { return _renderTarget.Bounds; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphicsDevice"></param>
        /// <param name="size"></param>
        public TextureBaker(GraphicsDevice graphicsDevice, Vector2 size) {

            _graphicsDevice = graphicsDevice;

            _batch = new SpriteBatch(_graphicsDevice);
            _renderTarget = new RenderTarget2D(
                _graphicsDevice, 
                (int)size.X, 
                (int)size.Y);

            _graphicsDevice.SetRenderTarget(_renderTarget);

            _graphicsDevice.Clear(Color.Transparent);

            _batch.Begin(
                SpriteSortMode.Immediate, 
                BlendState.AlphaBlend, 
                SamplerState.LinearClamp,
                DepthStencilState.Default, 
                RasterizerState.CullNone);
        }

        #region Texture2D baking

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        public void BakeTexture(Texture2D texture) {

            _batch.Draw(
                texture,
                new Rectangle(0, 0, Bounds.Width, Bounds.Height), 
                Color.White);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="destination"></param>
        public void BakeTexture(Texture2D texture, Rectangle destination) {

            _batch.Draw(
                texture,
                destination,
                Color.White);
        }        

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        public void BakeTexture(Texture2D texture, Rectangle destination, Rectangle source) {

            _batch.Draw(
                texture,
                destination,
                source,
                Color.White);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="sourceModification"></param>
        /// <param name="destination"></param>
        public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination) {

            Stream sourceBuffer = new MemoryStream();
            texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);

            System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);

            sourceBuffer = new MemoryStream();
            sourceImage.RotateFlip(sourceModification);
            sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);                       

            _batch.Draw(
                Texture2D.FromStream(_graphicsDevice, sourceBuffer),
                destination,
                Color.White);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="sourceModification"></param>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination, Rectangle source) {

            Stream sourceBuffer = new MemoryStream();
            texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);

            System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);

            sourceBuffer = new MemoryStream();
            sourceImage.RotateFlip(sourceModification);
            sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);

            _batch.Draw(
                Texture2D.FromStream(_graphicsDevice, sourceBuffer),
                destination,
                source,
                Color.White);
        }

        #endregion

        #region SpriteFont baking

        /// <summary>
        /// 
        /// </summary>
        /// <param name="font"></param>
        /// <param name="text"></param>
        /// <param name="location"></param>
        /// <param name="textColor"></param>
        public void BakeText(SpriteFont font, string text, Vector2 location, Color textColor) {

            _batch.DrawString(font, text, location, textColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="font"></param>
        /// <param name="text"></param>
        /// <param name="location"></param>
        public void BakeTextCentered(SpriteFont font, string text, Vector2 location, Color textColor) {

            var shifted = new Vector2 {
                X = location.X - font.MeasureString(text).X / 2,
                Y = location.Y - font.MeasureString(text).Y / 2
            };

            _batch.DrawString(font, text, shifted, textColor);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public Texture2D GetTexture() {

            _batch.End();
            _graphicsDevice.SetRenderTarget(null);

            return _renderTarget;
        }
    }
}
使用Microsoft.Xna.Framework;
使用Microsoft.Xna.Framework.Graphics;
使用System.IO;
命名空间Voodo.Utils{
/// 
/// 
/// 
公共类织纹机{
专用只读SpriteBatch\u批处理;
私有只读RenderTarget2D_renderTarget;
专用只读图形设备_GraphicsDevice;
/// 
/// 
/// 
公共矩形边界{
获取{return\u renderTarget.Bounds;}
}
/// 
/// 
/// 
/// 
/// 
公共纹理生成器(图形设备图形设备,矢量2大小){
_graphicsDevice=graphicsDevice;
_批次=新的SpriteBatch(_graphicsDevice);
_renderTarget=新建RenderTarget2D(
_图形设备,
(int)size.X,
(int)尺寸(Y);
_graphicsDevice.SetRenderTarget(_renderTarget);
_图形设备。清晰(颜色。透明);
_批处理,开始(
SpriteSortMode。立即,
BlendState.AlphaBlend,
samplestate.LinearClamp,
DepthStencilState.Default,
光栅化状态(CullNone);
}
#区域纹理2D烘焙
/// 
/// 
/// 
/// 
公共空间烘焙纹理(纹理2D纹理){
_分批抽签(
纹理
新矩形(0,0,Bounds.Width,Bounds.Height),
颜色(白色);
}
/// 
/// 
/// 
/// 
/// 
公共空心烘焙纹理(纹理2D纹理,矩形目标){
_分批抽签(
纹理
目的地,
颜色(白色);
}        
/// 
/// 
/// 
/// 
/// 
/// 
公共空心烘焙纹理(纹理2D纹理、矩形目标、矩形源){
_分批抽签(
纹理
目的地,
来源:,
颜色(白色);
}
/// 
/// 
/// 
///
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;

namespace Voodo.Utils {

    /// <summary>
    /// 
    /// </summary>
    public class TextureBaker {

        private readonly SpriteBatch _batch;
        private readonly RenderTarget2D _renderTarget;
        private readonly GraphicsDevice _graphicsDevice;

        /// <summary>
        /// 
        /// </summary>
        public Rectangle Bounds {
            get { return _renderTarget.Bounds; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphicsDevice"></param>
        /// <param name="size"></param>
        public TextureBaker(GraphicsDevice graphicsDevice, Vector2 size) {

            _graphicsDevice = graphicsDevice;

            _batch = new SpriteBatch(_graphicsDevice);
            _renderTarget = new RenderTarget2D(
                _graphicsDevice, 
                (int)size.X, 
                (int)size.Y);

            _graphicsDevice.SetRenderTarget(_renderTarget);

            _graphicsDevice.Clear(Color.Transparent);

            _batch.Begin(
                SpriteSortMode.Immediate, 
                BlendState.AlphaBlend, 
                SamplerState.LinearClamp,
                DepthStencilState.Default, 
                RasterizerState.CullNone);
        }

        #region Texture2D baking

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        public void BakeTexture(Texture2D texture) {

            _batch.Draw(
                texture,
                new Rectangle(0, 0, Bounds.Width, Bounds.Height), 
                Color.White);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="destination"></param>
        public void BakeTexture(Texture2D texture, Rectangle destination) {

            _batch.Draw(
                texture,
                destination,
                Color.White);
        }        

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        public void BakeTexture(Texture2D texture, Rectangle destination, Rectangle source) {

            _batch.Draw(
                texture,
                destination,
                source,
                Color.White);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="sourceModification"></param>
        /// <param name="destination"></param>
        public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination) {

            Stream sourceBuffer = new MemoryStream();
            texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);

            System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);

            sourceBuffer = new MemoryStream();
            sourceImage.RotateFlip(sourceModification);
            sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);                       

            _batch.Draw(
                Texture2D.FromStream(_graphicsDevice, sourceBuffer),
                destination,
                Color.White);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="sourceModification"></param>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination, Rectangle source) {

            Stream sourceBuffer = new MemoryStream();
            texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);

            System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);

            sourceBuffer = new MemoryStream();
            sourceImage.RotateFlip(sourceModification);
            sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);

            _batch.Draw(
                Texture2D.FromStream(_graphicsDevice, sourceBuffer),
                destination,
                source,
                Color.White);
        }

        #endregion

        #region SpriteFont baking

        /// <summary>
        /// 
        /// </summary>
        /// <param name="font"></param>
        /// <param name="text"></param>
        /// <param name="location"></param>
        /// <param name="textColor"></param>
        public void BakeText(SpriteFont font, string text, Vector2 location, Color textColor) {

            _batch.DrawString(font, text, location, textColor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="font"></param>
        /// <param name="text"></param>
        /// <param name="location"></param>
        public void BakeTextCentered(SpriteFont font, string text, Vector2 location, Color textColor) {

            var shifted = new Vector2 {
                X = location.X - font.MeasureString(text).X / 2,
                Y = location.Y - font.MeasureString(text).Y / 2
            };

            _batch.DrawString(font, text, shifted, textColor);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public Texture2D GetTexture() {

            _batch.End();
            _graphicsDevice.SetRenderTarget(null);

            return _renderTarget;
        }
    }
}