Engine.Context.StreamOutput.SetTarget(streamToBuffer, 0);
D3D11 WARNING: ID3D11DeviceContext::Begin: Begin is being invoked on a Query, where the previous results have not been obtained with GetData. This is valid; but unusual. The previous results are being abandoned, and new Query results will be generated. [ EXECUTION WARNING #408: QUERY_BEGIN_ABANDONING_PREVIOUS_RESULTS]
I'm not even using queries. If I turn off the debug layer, it stops on the line above it:Engine.Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(drawFromBuffer, ParticleVertex.SizeInBytes, 0));
"Additional information: External component has thrown an exception."
Full code:
[spoiler]
using Craft.Camera;
using Craft.Main;
using Craft.Misc;
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;
namespace Craft.Game.Particles
{
public struct ParticleVertex
{
public static readonly InputElement[] VertexDeclaration = new[]
{
new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
new InputElement("NORMAL", 0, Format.R32G32B32_Float, 0),
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 0),
new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0),
new InputElement("TEXCOORD", 1, Format.R32_UInt, 0),
new InputElement("TEXCOORD", 2, Format.R32G32_Float, 0)
};
public static readonly int SizeInBytes = Marshal.SizeOf(typeof(ParticleVertex));
public Vector3 Position;
public Vector3 Velocity;
public Vector4 Color;
public Vector2 TimerLifetime;
public uint Flags;
public Vector2 SizeStartEnd;
public ParticleVertex(Vector3 position, Vector3 velocity, Vector4 color, Vector2 timerLifetime, uint flags, Vector2 sizeStartEnd) : this()
{
Position = position;
Velocity = velocity;
Color = color;
TimerLifetime = timerLifetime;
Flags = flags;
SizeStartEnd = sizeStartEnd;
}
}
public static class GPUParticles
{
private const int MaxParticles = 1048576;
private const int MaxNew = 128;
[Flags]
enum ParticleFlags
{
Constrained = 1,
FastFade = 2
}
private static Buffer spawnBuffer;
private static Buffer drawFromBuffer;
private static Buffer streamToBuffer;
private static Effect effect;
private static EffectPass updatePass;
private static EffectPass renderPass;
private static InputLayout layout;
private static EffectMatrixVariable evView;
private static EffectMatrixVariable evProjection;
private static EffectMatrixVariable evLookAt;
private static EffectScalarVariable evTime;
private static EffectVectorVariable evCamDir;
private static EffectVectorVariable evGravity;
private static EffectShaderResourceVariable evTexture;
private static ParticleVertex[] newParticles;
private static int numNew;
internal static void Initialize()
{
newParticles = new ParticleVertex[MaxNew];
effect = ContentLoader.LoadEffect(@"Content\Effects\GPUParticles.fxo");
updatePass = effect.GetTechniqueByName("UpdateTeq").GetPassByIndex(0);
renderPass = effect.GetTechniqueByName("RenderTeq").GetPassByIndex(0);
layout = ContentLoader.CreateInputLayout(updatePass, ParticleVertex.VertexDeclaration);
spawnBuffer = new Buffer(Engine.Device, new BufferDescription(ParticleVertex.SizeInBytes * MaxNew, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0));
drawFromBuffer = new Buffer(Engine.Device, new BufferDescription(ParticleVertex.SizeInBytes * MaxParticles, ResourceUsage.Default, BindFlags.VertexBuffer | BindFlags.StreamOutput, CpuAccessFlags.None, ResourceOptionFlags.None, 0));
streamToBuffer = new Buffer(Engine.Device, new BufferDescription(ParticleVertex.SizeInBytes * MaxParticles, ResourceUsage.Default, BindFlags.VertexBuffer | BindFlags.StreamOutput, CpuAccessFlags.None, ResourceOptionFlags.None, 0));
evView = effect.GetVariableByName("View").AsMatrix();
evProjection = effect.GetVariableByName("Projection").AsMatrix();
evLookAt = effect.GetVariableByName("LookAt").AsMatrix();
evTime = effect.GetVariableByName("Time").AsScalar();
evCamDir = effect.GetVariableByName("CamDir").AsVector();
evGravity = effect.GetVariableByName("Gravity").AsVector();
evTexture = effect.GetVariableByName("Texture").AsShaderResource();
evTexture.SetResource(ContentLoader.LoadTexture(@"Content\Textures\particles\sparkle.png"));
}
public static 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 (numNew >= MaxNew)
return;
// create particle struct
var v = new ParticleVertex(position, velocity, color.ToVector4(), new Vector2(0, lifetime), 0, new Vector2(sizeStart, sizeEnd));
// set the particle's flags
if (constrained) v.Flags |= (uint) ParticleFlags.Constrained;
if (fastFade) v.Flags |= (uint) ParticleFlags.FastFade;
// append to buffer
newParticles[numNew++] = v;
}
internal static void Draw(ICamera camera)
{
var cameraForward = camera.Forward;
var lookAtMatrix = Matrix.Billboard(Vector3.Zero, -cameraForward, Vector3.UnitY, cameraForward);
evView.SetMatrix(camera.View);
evProjection.SetMatrix(camera.Projection);
evLookAt.SetMatrix(lookAtMatrix);
evTime.Set((float) Core.GameTime.TotalGameTime.TotalSeconds);
evCamDir.Set(cameraForward);
evGravity.Set(9.81f);
Advance();
Render();
}
private static void Advance()
{
// Use particleDrawFrom as input and stream output to particleStreamTo
Engine.Context.InputAssembler.InputLayout = layout;
Engine.Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
Engine.Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(drawFromBuffer, ParticleVertex.SizeInBytes, 0));
Engine.Context.StreamOutput.SetTarget(streamToBuffer, 0);
updatePass.Apply(Engine.Context);
// Update particles on the gpu
Engine.Context.DrawAuto();
// See if we have any newly-spawned particles waiting
if (numNew > 0)
{
// Copy new particles to vertex buffer and draw them to append them to stream output target
DataStream stream;
Engine.Context.MapSubresource(spawnBuffer, MapMode.WriteDiscard, MapFlags.None, out stream);
stream.WriteRange(newParticles);
Engine.Context.UnmapSubresource(spawnBuffer, 0);
Engine.Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(spawnBuffer, ParticleVertex.SizeInBytes, 0));
Engine.Context.Draw(numNew, 0);
numNew = 0;
}
Engine.Context.StreamOutput.SetTargets(null);
// Swap vertex buffers
Utilities.Swap(ref drawFromBuffer, ref streamToBuffer);
}
private static void Render()
{
// Since we just swapped the vertex buffers, particleDrawFrom contains the current state
Engine.Context.OutputMerger.SetBlendState(DeviceStates.BlendStateAdditive);
Engine.Context.OutputMerger.SetDepthStencilState(DeviceStates.DepthStencilStateNone);
Engine.Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(drawFromBuffer, ParticleVertex.SizeInBytes, 0));
renderPass.Apply(Engine.Context);
Engine.Context.DrawAuto();
Engine.Context.GeometryShader.Set(null);
}
internal static void Shutdown()
{
spawnBuffer.Dispose();
drawFromBuffer.Dispose();
streamToBuffer.Dispose();
}
}
}
[/spoiler]