GPGPU编程使用HLSL和XNA

XNA新手问题:

我试图让GPU为我执行一些计算。 我试图找出如何发送一些数据到GPU,让GPU来处理它,然后得到结果。 我修改了一些代码如下:

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; }; VertexShaderOutput VertexShaderFunction(VertexShaderInput vsInput) { VertexShaderOutput output; output.Position = vsInput.Position; output.TextureCoordinate = vsInput.TextureCoordinate; 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 = float4(100,200,13,24);//input0; return output; } technique Verlet { pass Go { VertexShader = compile vs_1_1 VertexShaderFunction(); PixelShader = compile ps_2_0 PixelShaderFunction(); } } 

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; namespace HLSLTest { /// <summary> /// This is the main type for your game /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D tex; RenderTarget2D renOutput; Vector4[] cpuStore = new Vector4[1920 * 1080]; Vector4[] gpuStore = new Vector4[1920 * 1080]; Effect effect; 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 cpuStore = new Vector4[1920 * 1080]; for (int i = 0; i < cpuStore.Length; i++) { cpuStore[i] = new Vector4(i); //gpuStore[i] = new Vector4(i); } 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); tex = new Texture2D(GraphicsDevice, 1920, 1080, false, SurfaceFormat.Vector4); renOutput = new RenderTarget2D(GraphicsDevice, 1920, 1080, false, SurfaceFormat.Vector4, DepthFormat.Depth24Stencil8); tex.SetData<Vector4>(cpuStore); 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) { //GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.SetRenderTarget(renOutput); //GraphicsDevice.Clear(Color.Black); effect.Techniques[0].Passes[0].Apply(); //effect.Parameters["oldPositionTexture"].SetValue(Output); effect.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.SetRenderTarget(null); renOutput.GetData<Vector4>(gpuStore); //tex.GetData<Vector4>(gpuStore); base.Draw(gameTime); } } } 

麻烦的是,当我在程序中放置一个断点并检查存储在gpuStore中的结果时,它包含一个大的vector列表{X:0.2666667 Y:0.1333333 Z:0.5333334 W:1}。 我期待它返回在我的着色器“float4(100,200,13,24)”中指定的值

编辑:

我已经做了一些修改感谢弥敦道的建议。 这是我现在拥有的:

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 = float4(100,200,13,24);//input0; return output; } technique Verlet { pass Go { VertexShader = compile vs_2_0 VertexShaderFunction(); PixelShader = compile ps_2_0 PixelShaderFunction(); } } 

C#

 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); } } } 

主程序

 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; namespace HLSLTest { /// <summary> /// This is the main type for your game /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D tex; RenderTarget2D renOutput; Vector4[] cpuStore = new Vector4[1920 * 1080]; 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 cpuStore = new Vector4[1920 * 1080]; for (int i = 0; i < cpuStore.Length; i++) { cpuStore[i] = new Vector4(100*i); //gpuStore[i] = new Vector4(i); } 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); tex = new Texture2D(GraphicsDevice, 1920, 1080, false, SurfaceFormat.Vector4); renOutput = new RenderTarget2D(GraphicsDevice, 1920, 1080, false, SurfaceFormat.Vector4, DepthFormat.Depth24); tex.SetData<Vector4>(cpuStore); 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) { //GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.SetRenderTarget(renOutput); //GraphicsDevice.Clear(Color.Black); effect.Parameters["Input0"].SetValue(cpuStore); 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(); } } //effect.Techniques[0].Passes[0].Apply(); //effect.Parameters["oldPositionTexture"].SetValue(Output); //effect.CurrentTechnique.Passes[0].Apply(); GraphicsDevice.SetRenderTarget(null); renOutput.GetData<Vector4>(gpuStore); //tex.GetData<Vector4>(gpuStore); base.Draw(gameTime); } } } 

现在我得到一个运行时错误

InvalidCastException未处理

指定的转换无效。

这是由于行效应。参数[“Input0”]。SetValue(cpuStore);

编辑

我可以解决这个问题,如果我可以从字节数组中创建我自己的Texture2D。

例如,如果我使用Emgu加载图像:

 Image<Bgr, Byte> video = cap.QueryFrame(); Texture2D t = new Texture2D(GraphicsDevice, video.Width, video.Height, false, SurfaceFormat.Color); t.SetData<byte>(video.Bytes); 

但是我明白了

ArgumentException是未处理的

传入的数据的大小对于此资源来说太大或太小。

最终版本

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(); } }