Yeah call stack is useless indeed. But the explanations might help. So the thing runs you just can't debug it with the graphics debugger. (Lapse on my part, you did mention it already, I just didn't understand it fails with the debugger only). Welcome to the club: If memory serves, I could never debug StreamOut with PIX either.
That leaves only one thing: Debug yourself. Setup a staging buffer, use context.CopyResource to copy the stream out buffer to the stage after the update and context.Map the stage to read it back. Then inspect it or log it. To make it easier modify the system to use only one particle (one that never dies e.g.). Or draw a small mesh you know that draws at said position. That should cover the update part. It's a bit involved, but that's about what I can come up with (other than using a different tool that hopefully works).
You said you use CPU-particles now, so I assume you can draw them. Do you use the drawing technique from the sample (meaning: a quad expansion geometry shader) or something else ?
Well its not that I can't debug the streamout, I can't debug anything in my entire project because the particle system just kills the debugger. Of course I can disable it for debugging other systems, but I just wanted to point out that its not specifically when inspecting the particle system, the entire snapshot fails. More importantly, the debugger actually works just fine when run on the sample. I will give the manual debugging idea a try, but there's a lot of things that could be wrong that will be hard to spot without a proper debugger.
And yes, my CPU particles draw just fine. I'm not using any of the code/shaders from them for this though, I'm using the same drawing code/shader from the sample.
I've compared my code with what GuoLei posted and its pretty much identical, especially the part that's crashing for me, so I'm really not sure what's wrong with it. As a random though, I wonder if this has anything to do with the fact that my project is using DX11.1? Also here is my shader code, though I doubt the issue is coming from there:
//
// Particle effect using geometry shader and stream out
// 2013 Christoph Romstoeck (lwm)
//
Texture2D<float4> Texture;
SamplerState linearSampler
{
AddressU = CLAMP;
AddressV = CLAMP;
Filter = MIN_MAG_MIP_LINEAR;
};
#define FLAG_CONSTRAINED 1
#define FLAG_FAST_FADE 2
cbuffer EveryFrame : register(b0)
{
float4x4 View;
float4x4 Projection;
float4x4 LookAt;
float3 CamDir;
float Time;
float3 Gravity;
};
struct ParticleVertex
{
float3 Position : POSITION;
float3 Velocity : NORMAL;
float4 Color : COLOR;
float2 TimerLifetime : TEXCOORD0;
uint Flags : TEXCOORD1;
float2 SizeStartEnd : TEXCOORD2;
};
struct ParticleVertexGsUpdateOut
{
float4 Position : SV_POSITION;
float3 Velocity : NORMAL;
float4 Color : COLOR;
float2 TimerLifetime : TEXCOORD0;
uint Flags : TEXCOORD1;
float2 SizeStartEnd : TEXCOORD2;
};
struct ParticleVertexGsOut
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
float2 TexCoord : TEXCOORD0;
float4 PositionVS : TEXCOORD1;
};
// ===
// Vertex shader has no work to do.
// Simply pass vertex on to the next stage.
ParticleVertex VS_Passthrough(ParticleVertex v)
{
return v;
}
// Geometry shader to update one particle.
[maxvertexcount(1)]
void GS_Update(point ParticleVertex vertex[1], inout PointStream<ParticleVertexGsUpdateOut> stream) {
ParticleVertex input = vertex[0];
// Calculate new age of the particle.
float newTimer = input.TimerLifetime.x + Time;
// If the particle is older than its lifetime, don't do anything.
if(newTimer > input.TimerLifetime.y)
return;
// Calculate new position by adding the particle's velocity.
float3 newPosition = input.Position + input.Velocity * Time;
// Calculate new velocity by adding the world's gravity.
float3 newVelocity = input.Velocity + Gravity * Time;
ParticleVertexGsUpdateOut output;
output.Position = float4(newPosition, 1);
output.Velocity = newVelocity;
output.Color = input.Color;
output.TimerLifetime.x = newTimer;
output.TimerLifetime.y = input.TimerLifetime.y;
output.Flags = input.Flags;
output.SizeStartEnd = input.SizeStartEnd;
// Append updated particle to output stream.
stream.Append(output);
}
GeometryShader pGSComp = CompileShader(gs_4_0, GS_Update());
GeometryShader pGSwSO = ConstructGSWithSO(pGSComp, "SV_POSITION.xyz; NORMAL.xyz; COLOR.xyzw; TEXCOORD0.xy; TEXCOORD1.x; TEXCOORD2.xy");
technique11 UpdateTeq
{
pass Pass1
{
SetVertexShader(CompileShader(vs_4_0, VS_Passthrough()));
SetGeometryShader(pGSwSO);
SetPixelShader(NULL);
}
}
// ===============================================
// Geometry shader to expand the vertex into a quad.
[maxvertexcount(4)]
void GS_Render(point ParticleVertex inputArray[1], inout TriangleStream<ParticleVertexGsOut> stream)
{
ParticleVertex input = inputArray[0];
// Calculate the particles age in [0..1]
float age = input.TimerLifetime.x / input.TimerLifetime.y;
ParticleVertexGsOut v;
// Determine the particle's color based on its age.
v.Color = input.Color;
bool fastFade = (input.Flags & FLAG_FAST_FADE) > 0;
if (fastFade)
v.Color.a *= (-(256 * 256) * pow(age - 0.5f, 16) + 1);
else
v.Color.a *= (-4 * (age - 0.5f) * (age - 0.5f) + 1);
// Calculate the particle's current size
float2 size = lerp(input.SizeStartEnd.x, input.SizeStartEnd.y, age);
// Check if one of the quad's axes should be constrained to the particle's velocity.
bool constrained = (input.Flags & FLAG_CONSTRAINED) > 0;
float3 right, up;
if(constrained)
{
right = normalize(input.Velocity);
up = cross(CamDir, right) * size.y;
right *= size.x;
}
else
{
float2 xr = float4(size.x, 0, 0, 1);
float2 yr = float4(0, size.y, 0, 1);
right = mul(xr, LookAt).xyz;
up = mul(yr, LookAt).xyz;
}
// Create and append four vertices to form a quad.
float4 positionWS = float4(input.Position + right + up, 1.f);
v.PositionVS = mul(positionWS, View);
v.Position = mul(v.PositionVS, Projection);
v.TexCoord = float2(1, 1);
stream.Append(v);
positionWS = float4(input.Position - right + up, 1.f);
v.PositionVS = mul(positionWS, View);
v.Position = mul(v.PositionVS, Projection);
v.TexCoord = float2(0, 1);
stream.Append(v);
positionWS = float4(input.Position + right - up, 1.f);
v.PositionVS = mul(positionWS, View);
v.Position = mul(v.PositionVS, Projection);
v.TexCoord = float2(1, 0);
stream.Append(v);
positionWS = float4(input.Position - right - up, 1.f);
v.PositionVS = mul(positionWS, View);
v.Position = mul(v.PositionVS, Projection);
v.TexCoord = float2(0, 0);
stream.Append(v);
stream.RestartStrip();
}
// Simple pixel shader to render the particles.
float4 PS_Render(ParticleVertexGsOut input) : SV_Target
{
float4 tex = Texture.Sample(linearSampler, input.TexCoord);
return tex * input.Color;
}
technique11 RenderTeq
{
pass Pass1
{
SetVertexShader(CompileShader(vs_4_0, VS_Passthrough()));
SetGeometryShader(CompileShader(gs_4_0, GS_Render()));
SetPixelShader(CompileShader(ps_4_0, PS_Render()));
}
}