在XNA中使用HLSL

本文关键字:HLSL XNA | 更新日期: 2023-09-27 18:22:33

我想写一个perona-malik各向异性滤波器,出于性能原因,我似乎需要使用gpu。长话短说,我想知道如何使用HLSL在xna到GPGPU的任务。

我正在寻找一些代码片段,将一些数据移动到GPU上,逐帧处理,然后返回,这样我就可以用它做其他事情。

从我读到的内容来看,我需要使用"乒乓"

编辑

这就是我目前拥有的

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
namespace HotplateTest
{
    public class XNAClass : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        RenderTarget2D Target;
        RenderTarget2D Output;
        Effect physicsEffect;
        Vector4[] positions;
        public XNAClass()
        {
            graphics = new GraphicsDeviceManager(this);
        }
        protected override void Initialize()
        {
            Target = new RenderTarget2D(graphics.GraphicsDevice, 10, 10, false, SurfaceFormat.Vector4, DepthFormat.None);
            Output = new RenderTarget2D(graphics.GraphicsDevice, 10, 10, false, SurfaceFormat.Vector4, DepthFormat.None);
            positions = new Vector4[100];
            for (int i = 0; i < positions.Length; i++)
            {
                positions[i] = new Vector4(i);
            }
            Target.SetData<Vector4>(positions);
            base.Initialize();
        }
        protected override void LoadContent()
        {
            base.LoadContent();
            physicsEffect = Content.Load<Effect>("shader");
        }
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.SetRenderTarget(Target); 
            GraphicsDevice.Clear(Color.Black);
            physicsEffect.Techniques[0].Passes[0].Apply();
            physicsEffect.Parameters["oldPositionTexture"].SetValue(Output); 
            physicsEffect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.SetRenderTarget(null);
            Target.GetData<Vector4>(positions);

            base.Draw(gameTime);
        }
    }
}

HLSL

texture oldPositionTexture;
sampler oldPositionSampler = sampler_state
{
    Texture = < oldPositionTexture >;
    MipFilter = POINT;
    MinFilter = POINT;
    MagFilter = POINT;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};
struct VertexShaderInput
{
    float4 Position : POSITION;
    float2 Tex  : TEXCOORD0;
};
struct VertexShaderOutput
{
    float4 Position : POSITION;
    float2 Tex  : TEXCOORD0;
};
// input texture dimensions
static const float w = 10;
static const float h = 10;
static const float2 pixel = float2(1.0 / w, 1.0 / h);
static const float2 halfPixel = float2(pixel.x / 2, pixel.y / 2);
VertexShaderOutput VS(VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;
    output.Tex = input.Tex; 
    return output;
}
float4 PS1(VertexShaderOutput input) : COLOR0
{
    float2 myV = input.Tex;
    float myPosAndMass = tex2D(oldPositionSampler, myV);
    return float4(myPosAndMass, myPosAndMass, myPosAndMass, myPosAndMass);
}
technique Technique1
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS();
        PixelShader = compile ps_2_0 PS1();
    }
}

我收到错误信息

中发生类型为"System.ArgumentException"的未处理异常Microsoft.Xna.Framework.Graphics.dll

附加信息:此方法中用于T的类型为此资源的大小无效。

有趣的是,当我在C#代码中使用Single而不是Vector4时,不会出现错误消息。当然,这意味着在位置变量中计算的结果现在是无用的,因为当它们真的是向量4时,它们被解释为单个。

在XNA中使用HLSL

C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Emgu.Util;
using Emgu.CV;
using Emgu.CV.Structure;
namespace HLSLTest
{
    public delegate void Disp();
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        Capture cap = new Capture("output.avi");
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        RenderTarget2D renOutput;
        Vector4[] gpuStore = new Vector4[1920 * 1080];
        Effect effect;
        QuadRender quad;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
        }
        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            quad = new QuadRender(GraphicsDevice);
            renOutput = new RenderTarget2D(GraphicsDevice, 1920, 1080, false, SurfaceFormat.Vector4, DepthFormat.Depth24);
            effect = Content.Load<Effect>("Shader");
            base.LoadContent();
            // TODO: use this.Content to load your game content here
        }
        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            // TODO: Add your update logic here
            base.Update(gameTime);
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            Image<Bgr, Byte> video = cap.QueryFrame();
            using (Image<Bgra, float> vid2 = video.Convert<Bgra, float>())
            {
                Texture2D t = new Texture2D(GraphicsDevice, video.Width, video.Height, false, SurfaceFormat.Vector4);
                t.SetData<byte>(vid2.Bytes);
                GraphicsDevice.SetRenderTarget(renOutput);
                effect.Parameters["Input0"].SetValue(t);
                quad.RenderFullScreenQuad(effect);
                for (int i = 0; i < effect.Techniques.Count; i++)
                {
                    for (int j = 0; j < effect.Techniques[i].Passes.Count; j++)
                    {
                        effect.Techniques[i].Passes[j].Apply();
                    }
                }
                GraphicsDevice.SetRenderTarget(null);
                renOutput.GetData<Vector4>(gpuStore);
            }
            base.Draw(gameTime);
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace HLSLTest
{
    internal sealed class QuadRender
    {
        private VertexPositionTexture[] verts;
        private GraphicsDevice myDevice;
        private short[] ib = null;
        ///
        /// Loads the quad.
        ///
        ///
        public QuadRender(GraphicsDevice device)
        {
            myDevice = device;         
            verts = new VertexPositionTexture[]
            {
                new VertexPositionTexture
                (
                    new Vector3(0,0,0),
                    new Vector2(1,1)
                ),
                new VertexPositionTexture
                (
                    new Vector3(0,0,0),
                    new Vector2(0,1)
                ),
                new VertexPositionTexture
                (
                    new Vector3(0,0,0),
                    new Vector2(0,0)
                ),
                new VertexPositionTexture
                (
                    new Vector3(0,0,0),
                    new Vector2(1,0)
                )
            };
            ib = new short[] { 0, 1, 2, 2, 3, 0 };
        }             
        ///
        /// Draws the fullscreen quad.
        ///
        ///
        public void RenderFullScreenQuad(Effect effect)
        {
            effect.CurrentTechnique.Passes[0].Apply();
            RenderQuad(Vector2.One * -1, Vector2.One);
        }
        public void RenderQuad(Vector2 v1, Vector2 v2)
        {          
            verts[0].Position.X = v2.X;
            verts[0].Position.Y = v1.Y;
            verts[1].Position.X = v1.X;
            verts[1].Position.Y = v1.Y;
            verts[2].Position.X = v1.X;
            verts[2].Position.Y = v2.Y;
            verts[3].Position.X = v2.X;
            verts[3].Position.Y = v2.Y;
            myDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, verts, 0, 4, ib, 0, 2);
        }
    }
}

HLSL

texture2D Input0;
sampler2D Input0Sampler = sampler_state
{
    Texture = <Input0>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Clamp;
    AddressV = Clamp;
};
texture2D Input1;
sampler2D Input1Sampler = sampler_state
{
    Texture = <Input1>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Clamp;
    AddressV = Clamp;
};
texture2D Input2;
sampler2D Input2Sampler = sampler_state
{
    Texture = <Input2>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Clamp;
    AddressV = Clamp;
};
texture2D Input3;
sampler2D Input3Sampler = sampler_state
{
    Texture = <Input3>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Clamp;
    AddressV = Clamp;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TextureCoordinate : TEXCOORD0;
};
struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 TextureCoordinate : TEXCOORD0;
};
struct PixelShaderOutput
{
    // TODO: Optionally add/remove output indices to match GPUProcessor.numOutputs
    float4 Index0 : COLOR0;
};
// input texture dimensions
static const float w = 1920;
static const float h = 1080;
static const float2 pixel = float2(1.0 / w, 1.0 / h);
static const float2 halfPixel = float2(pixel.x / 2, pixel.y / 2);

VertexShaderOutput VertexShaderFunction(VertexShaderInput vsInput)
{
    //VertexShaderOutput output;
    //output.Position = vsInput.Position;
    //output.TextureCoordinate = vsInput.TextureCoordinate;
    VertexShaderOutput output;
    vsInput.Position.x =  vsInput.Position.x - 2*halfPixel.x;
    vsInput.Position.y =  vsInput.Position.y + 2*halfPixel.y;
    output.Position = vsInput.Position;
    output.TextureCoordinate = vsInput.TextureCoordinate ;
    return output;

    //return output;
}
PixelShaderOutput PixelShaderFunction(VertexShaderOutput psInput)
{
    PixelShaderOutput output;
    // TODO: Optionally add/remove samples to match GPUProcessor.numInputs
    float4 input0 = tex2D(Input0Sampler, psInput.TextureCoordinate);
    //float4 input1 = tex2D(Input0Sampler, psInput.TextureCoordinate);
    //float4 input2 = tex2D(Input0Sampler, psInput.TextureCoordinate);
    //float4 input3 = tex2D(Input0Sampler, psInput.TextureCoordinate);
    // your calculations go here
    // TODO: Optionally add/remove outputs to match GPUProcessor.numOutputs
    output.Index0 = input0;//float4(100,200,13,24);//input0;
    return output;
}
technique Verlet
{
    pass Go
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}