GPU particles

Started by
38 comments, last by Telanor 10 years, 8 months ago

I'm currently having this exact same issue in my SlimDX code. Did you ever find a solution to that problem?


No I never found a solution. The error makes no sense to me and I can't find any info about it on google, so I'm stuck with CPU particles for the time being.
Advertisement

I'm currently having this exact same issue in my SlimDX code. Did you ever find a solution to that problem?


No I never found a solution. The error makes no sense to me and I can't find any info about it on google, so I'm stuck with CPU particles for the time being.

Oh! I am very sad to hear this..... we both use sharpdx11.......I also can't find any info about this!!

The info is in the MSDN doc, you're just misreading the compiler error. Admittedly, setting up SO in the effect file is a little cryptic.

parameter count mismatch (ConstructGSWithSO)

There are up to four streams from your geometry shader you can select from, not just three. You're missing a NULL.

Also, if I get the sample right, it uses one output stream only, so you can omit the (optional) buffer index altogether (an index of >3 e.g. doesn't make sense anyway, you only got 4 output streams).

This should work (at least it compiles)


GeometryShader gsStreamOut = ConstructGSWithSO( pGSComp, "SV_POSITION.xyz;NORMAL.xyz;COLOR.xyzw;TEXCOORD0.xy;TEXCOORD1.x;TEXCOORD2.xy;",NULL,NULL,NULL,-1);
Note the -1 at the end. This is actually D3D11_SO_NO_RASTERIZED_STREAM, since you don't need rasterization for the particle update. Not sure this will work, try 0 first instead (PixelShader is NULL anyway).

The info is in the MSDN doc, you're just misreading the compiler error. Admittedly, setting up SO in the effect file is a little cryptic.

parameter count mismatch (ConstructGSWithSO)

There are up to four streams from your geometry shader you can select from, not just three. You're missing a NULL.

Also, if I get the sample right, it uses one output stream only, so you can omit the (optional) buffer index altogether (an index of >3 e.g. doesn't make sense anyway, you only got 4 output streams).

This should work (at least it compiles)


GeometryShader gsStreamOut = ConstructGSWithSO( pGSComp, "SV_POSITION.xyz;NORMAL.xyz;COLOR.xyzw;TEXCOORD0.xy;TEXCOORD1.x;TEXCOORD2.xy;",NULL,NULL,NULL,-1);
Note the -1 at the end. This is actually D3D11_SO_NO_RASTERIZED_STREAM, since you don't need rasterization for the particle update. Not sure this will work, try 0 first instead (PixelShader is NULL anyway).

Tanks! I have write that like you said But sitll failed! I find the particle but it only one pixel! I don't know why Can you analysis it by your Experience!

There are two techniques (and two geometry shaders) in that example. One for the particle update, one for drawing. You probably misuse the update for drawing. As said, disable rasterization for the update technique.

Streamout uses point streams, that's why you get those single pixels. The draw pass expands those points to quads (two triangles).

Since I'm now caught here:

@Telanor: Two concerns: Grant your ParticleVertex a proper StructLayout (like in the original code), otherwise Marshal.SizeOf may go wrong. Also: Why is GPUParticles a static class ?

@Telanor: Two concerns: Grant your ParticleVertex a proper StructLayout (like in the original code), otherwise Marshal.SizeOf may go wrong. Also: Why is GPUParticles a static class ?


Yea I noticed I missed the StructLayout after I posted that. I've since added it, it didn't make any difference though. GPUParticles is a static class because I was planning on running all the particles from that class.

The info is in the MSDN doc, you're just misreading the compiler error. Admittedly, setting up SO in the effect file is a little cryptic.


parameter count mismatch (ConstructGSWithSO)

There are up to four streams from your geometry shader you can select from, not just three. You're missing a NULL.

Also, if I get the sample right, it uses one output stream only, so you can omit the (optional) buffer index altogether (an index of >3 e.g. doesn't make sense anyway, you only got 4 output streams).

This should work (at least it compiles)


GeometryShader gsStreamOut = ConstructGSWithSO( pGSComp, "SV_POSITION.xyz;NORMAL.xyz;COLOR.xyzw;TEXCOORD0.xy;TEXCOORD1.x;TEXCOORD2.xy;",NULL,NULL,NULL,-1);
Note the -1 at the end. This is actually D3D11_SO_NO_RASTERIZED_STREAM, since you don't need rasterization for the particle update. Not sure this will work, try 0 first instead (PixelShader is NULL anyway).

I have search disable rasterization you said that is SetPixelShader(NULL) in shader.Second make both the depthState and StencilState disable! I want konw is the right way to realize you said like this?

Now I have success with your help.. I am very thanks to you! you are great...But I doubt is the right way to do like this

173985025201307252228022158823211327_000

@Telanor: Two concerns: Grant your ParticleVertex a proper StructLayout (like in the original code), otherwise Marshal.SizeOf may go wrong. Also: Why is GPUParticles a static class ?


Yea I noticed I missed the StructLayout after I posted that. I've since added it, it didn't make any difference though. GPUParticles is a static class because I was planning on running all the particles from that class.

I has success I will give the code to you maybe the elapsedTime you are wrong


using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System;
using System.Runtime.InteropServices;
using Buffer = SharpDX.Direct3D11.Buffer;
using MapFlags = SharpDX.Direct3D11.MapFlags;
using MoonLight.Graphics;
using MoonLight.Vertex;
using MoonLight.Scene;
using MoonLight.Dispose;
using MoonLight.Time;

namespace MoonLight.Graphics.Particle
{
   

    public  class ParticleSystem:IMovableObj, IRenderableObj, IDisposable
    {

        BasicScene m_BasicScene;

        string m_Name;
        public string Name
        {
            get { return m_Name; }
        }

        SceneNode m_ParentSceneNode;

        public SceneNode ParentSceneNode
        {
            get { return m_ParentSceneNode; }
            set { m_ParentSceneNode = value; }
        }

       
        private const int MaxParticles = 1024 * 16;
        private const int MaxNew = 128;

        public const uint Flag_Constrained = 1;
        public const uint Flag_Fast_Fade = 2;

        private  Buffer m_SpawnBuffer;
        private  Buffer m_DrawFromBuffer;
        private  Buffer M_StreamToBuffer;

        private  EffectPass m_UpdatePass;
        private  EffectPass m_RenderPass;
        private  InputLayout m_InputLayout;
        ShaderResourceView m_ParticleTextureView;
        Effect m_Effect;

        private  VertexParticle[] m_NewParticles;
        private  int m_NumNew=0;
      

        private Random m_Random = new Random();
        private float m_Elapsed;


        public ParticleSystem(BasicScene basicScene, string name, Effect effect, Texture2D particleTexture2D)
        {
            this.m_Name = name;
            m_BasicScene = basicScene;
            m_NewParticles = new VertexParticle[MaxNew];

            m_SpawnBuffer = new Buffer(m_BasicScene.WPFRenderHost.Device, new BufferDescription(VertexParticle.SizeInBytes * MaxNew, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0));
            m_DrawFromBuffer = new Buffer(m_BasicScene.WPFRenderHost.Device, new BufferDescription(VertexParticle.SizeInBytes * MaxParticles, ResourceUsage.Default, BindFlags.VertexBuffer | BindFlags.StreamOutput, CpuAccessFlags.None, ResourceOptionFlags.None, 0));
            M_StreamToBuffer = new Buffer(m_BasicScene.WPFRenderHost.Device, new BufferDescription(VertexParticle.SizeInBytes * MaxParticles, ResourceUsage.Default, BindFlags.VertexBuffer | BindFlags.StreamOutput, CpuAccessFlags.None, ResourceOptionFlags.None, 0));

            m_UpdatePass = effect.GetTechniqueByName("UpdateTeq").GetPassByIndex(0);
            m_RenderPass = effect.GetTechniqueByName("RenderTeq").GetPassByIndex(0);

            m_InputLayout = new InputLayout(m_BasicScene.WPFRenderHost.Device, m_UpdatePass.Description.Signature, VertexParticle.VertexDeclaration);
            m_Effect = effect;
            m_ParticleTextureView = new ShaderResourceView(m_BasicScene.WPFRenderHost.Device, particleTexture2D);

        
        
        }


        public void Spawn(Vector3 position, Vector3 velocity, Color color, float sizeStart, float sizeEnd, float lifetime, bool constrained, bool fastFade)
        {
            // discard particle if buffer is full 
            if (m_NumNew >= MaxNew)
                return;

            // create particle struct 
            var v = new VertexParticle(position, velocity, color.ToVector4(), new Vector2(0, lifetime), 0, new Vector2(sizeStart, sizeEnd));

            // set the particle's flags 
            if (constrained) 
                v.Flags |= Flag_Constrained;
            if (fastFade) 
                v.Flags |= Flag_Fast_Fade;

            // append to buffer 
            m_NewParticles[m_NumNew++] = v;
        }


        public void AddToRenderQneue(RenderQueue renderQneue)
        {
            renderQneue.AddRenderableObj(this);
        }

        private Vector3 RandomDirection(Vector3 direction, float max)
        {
            float r1 = ((float)m_Random.NextDouble() * 2 - 1) * max;
            float r2 = ((float)m_Random.NextDouble() * 2 - 1) * max;
            float r3 = ((float)m_Random.NextDouble() * 2 - 1) * max;

            Quaternion q = Quaternion.RotationYawPitchRoll(r1, r2, r3);
            return Vector3.Transform(direction, q);
        }

        public void Draw(RenderTimer renderTimer)
        {
            if (m_BasicScene.SceneManager.RenderType == RenderType.Normal)
            {
                for (int i = 0; i < 128; i++)
                {
                    Vector3 position = Vector3.Zero;
                    Vector3 velocity = RandomDirection(Vector3.UnitY, MathUtil.TwoPi) * (float)m_Random.NextDouble(1.0, 4.0);
                    Color color = new Color((float)m_Random.NextDouble(), (float)m_Random.NextDouble(), (float)m_Random.NextDouble());
                    float startSize = 0.02f;
                    float endSize = 0.2f;
                    float lifetime = 2f;

                    Spawn(Vector3.Zero, velocity, color, startSize, endSize, lifetime, false, false);
                }
                m_Elapsed += ((float)renderTimer.IntervalMillseconds) / 1000;

                Advance();
                Render();
            }
        }
        void Advance()
        {
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.OutputMerger.SetDepthStencilState(m_BasicScene.WPFRenderHost.DeviceStates.NoneDepthStencilState);
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.InputLayout = m_InputLayout;
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(m_DrawFromBuffer, VertexParticle.SizeInBytes, 0));
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.StreamOutput.SetTarget(M_StreamToBuffer, 0);

            m_UpdatePass.Apply(m_BasicScene.WPFRenderHost.Device.ImmediateContext);

            // Update particles on the gpu 
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.DrawAuto();

            // See if we have any newly-spawned particles waiting 
            if (m_NumNew > 0)
            {
                // Copy new particles to vertex buffer and draw them to append them to stream output target 
                DataStream stream;
                m_BasicScene.WPFRenderHost.Device.ImmediateContext.MapSubresource(m_SpawnBuffer, MapMode.WriteDiscard, MapFlags.None, out stream);
                stream.WriteRange(m_NewParticles);
                m_BasicScene.WPFRenderHost.Device.ImmediateContext.UnmapSubresource(m_SpawnBuffer, 0);
                m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
                m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(m_SpawnBuffer, VertexParticle.SizeInBytes, 0));
                m_BasicScene.WPFRenderHost.Device.ImmediateContext.Draw(m_NumNew, 0);
                m_NumNew = 0;
            }

            m_BasicScene.WPFRenderHost.Device.ImmediateContext.StreamOutput.SetTargets(null);

            // Swap vertex buffers 
            Utilities.Swap(ref m_DrawFromBuffer, ref M_StreamToBuffer);
        }

        void Render()
        {
            // Since we just swapped the vertex buffers, particleDrawFrom contains the current state 
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.OutputMerger.SetBlendState(m_BasicScene.WPFRenderHost.DeviceStates.AdditiveState);
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.OutputMerger.SetDepthStencilState(m_BasicScene.WPFRenderHost.DeviceStates.NoneDepthState);
            Vector3 cameraForward = m_BasicScene.SceneManager.Camera.Direction;
            Matrix lookAtMatrix = Matrix.Billboard(Vector3.Zero, m_BasicScene.SceneManager.Camera.Position, Vector3.UnitY, cameraForward);
            m_Effect.GetVariableByName("_view").AsMatrix().SetMatrix(m_BasicScene.SceneManager.Camera.View);
            m_Effect.GetVariableByName("_proj").AsMatrix().SetMatrix(m_BasicScene.SceneManager.Camera.Projection);
            m_Effect.GetVariableByName("_lookAtMatrix").AsMatrix().SetMatrix(lookAtMatrix);
            m_Effect.GetVariableByName("_elapsedSeconds").AsScalar().Set(m_Elapsed);
            m_Effect.GetVariableByName("_camDir").AsVector().Set(cameraForward);
            m_Effect.GetVariableByName("_gravity").AsVector().Set(9.81f);
            m_Effect.GetVariableByName("_texture").AsShaderResource().SetResource(m_ParticleTextureView);
            this.m_Elapsed = 0;


            m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(m_DrawFromBuffer, VertexParticle.SizeInBytes, 0));
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.InputLayout = m_InputLayout;
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
            m_RenderPass.Apply(m_BasicScene.WPFRenderHost.Device.ImmediateContext);
            m_BasicScene.WPFRenderHost.Device.ImmediateContext.DrawAuto();

            m_BasicScene.WPFRenderHost.Device.ImmediateContext.GeometryShader.Set(null);
        }

        public void Dispose()
        {
            Disposer.RemoveAndDispose(ref m_SpawnBuffer);
            Disposer.RemoveAndDispose(ref m_DrawFromBuffer);
            Disposer.RemoveAndDispose(ref M_StreamToBuffer);
            Disposer.RemoveAndDispose(ref m_ParticleTextureView);
            Disposer.RemoveAndDispose(ref m_InputLayout);
        }
    }
}

I have search disable rasterization you said that is SetPixelShader(NULL) in shader.Second make both the depthState and StencilState disable! I want konw is the right way to realize you said like this?
Now I have success with your help.. I am very thanks to you! you are great...But I doubt is the right way to do like this

You're welcome. Looks like the sample, so yeah, I think it's fine. Not sure if I understand your question (you might try google translate - no offense intended). Disabling depth write is ok. To correctly occlude particles you need to draw your other stuff first and the draw the particles with depth test only.

@Telanor:
I just have a gut feeling here. I suspect sort of a race condition or something with the static initialization. Does the sample work at all ? Could you try a non-static version ? I'm shooting in the dark: Do you have more - useful - information about that crash ? Call stack ? D3D debug log ?
Yea the sample works perfectly fine, even in the VS graphics debugger. I tried switching to a non-static version and it still has the same problem. I'd love to provide you with any information I can but I'm not sure there's much to be had. Let me explain in more detail what's going on:

When I run the game normally, after the world loads up, I press a key to spawn 50k particles around me, the code runs, but I see absolutely nothing. Maybe their position is wrong, maybe they're too small, I don't know. So I try to run the VS graphics debugger, start up the game again, spawn the particles, press print screen and then it halts. If I leave the directx debug layer off, it just gives me a generic "External component threw an error" message.

Next I go turn on the debug layer and start it up again (without the graphics debugger). It runs without errors again. Starting it up with the graphics debugger, it now halts before I even have a chance to spawn any particles. It stops on the StreamOutput.SetTarget line in the `Advance` method, giving that warning about the query thing. I've tried making it so draw calls called before any particles have spawned don't run any code, but that still causes the same error once I do spawn the particles.

This is the CPU callstack, I can't imagine it'll be of much help:

[attachment=16994:callstack.png]

I can't get the GPU callstack that the graphics debugger lists since it crashes before I can take a snapshot. I'm not sure where I'd get a D3D debug log from. If you can tell me how to I'll post that up as well.

This topic is closed to new replies.

Advertisement