Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ParticlePlugin.cs
Go to the documentation of this file.
1 // Copyright (c) 2011 Silicon Studio
2 
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Runtime.InteropServices;
7 using SiliconStudio.Paradox.Effects.Modules;
8 using SiliconStudio.Paradox.Engine;
9 using SiliconStudio.Paradox.Games;
10 using SiliconStudio.Paradox.Graphics;
11 using SiliconStudio.Core;
12 using SiliconStudio.Core.Collections;
13 using SiliconStudio.Core.Mathematics;
14 using SiliconStudio.Paradox.Shaders;
15 
17 
18 namespace SiliconStudio.Paradox.Effects
19 {
21  {
22  private int capacityCount;
23 
24  private int updatersCount;
25 
26  private RenderPass updatePasses;
27 
28  private RenderPass copyPass;
29 
30  private RenderPass sortPass;
31 
32  private RenderPass bitonicSort1Pass1;
33 
34  private RenderPass bitonicSort1Pass2;
35 
36  private RenderPass[] bitonicSort2Passes;
37 
38  private RenderPass renderPass;
39 
40  private Buffer globalBuffer;
41 
42  private Buffer sortBuffer;
43 
44  private readonly List<ParticleUpdaterState> updatersToRemove;
45 
46  private readonly List<ParticleUpdaterState> currentUpdaters;
47 
48  private readonly RenderPassListEnumerator meshesToRender;
49 
50  private EffectMesh effectMeshCopyToSortBuffer;
51 
52  private EffectMesh effectMeshSort1Pass1;
53  private EffectMesh effectMeshSort1Pass2;
54  private EffectMesh[] effectMeshBitonicSort2;
55  private EffectMesh effectMeshRender;
56 
57  private const int MaximumThreadPerGroup = 512;
58  private const int MaximumDepthLevel = 512;
59 
60  private EffectOld effectCopyToSortBuffer;
61  private EffectOld effectBitonicSort1Pass1;
62  private EffectOld effectBitonicSort1Pass2;
63 
64  private EffectOld[] effectBitonicSort2;
65 
66  private int currentParticleCount = 0;
67 
68  private IGraphicsDeviceService graphicsDeviceService;
69 
70  /// <summary>
71  /// Initializes a new instance of the <see cref="ParticlePlugin" /> class.
72  /// </summary>
73  public ParticlePlugin() : this(null)
74  {
75  }
76 
77  /// <summary>
78  /// Initializes a new instance of the <see cref="ParticlePlugin" /> class.
79  /// </summary>
80  /// <param name="name">Name of this particle manager</param>
81  public ParticlePlugin(string name) : base(name)
82  {
83  meshesToRender = new RenderPassListEnumerator();
84  Updaters = new TrackingCollection<ParticleEmitterComponent>();
85  currentUpdaters = new List<ParticleUpdaterState>();
86  updatersToRemove = new List<ParticleUpdaterState>();
87  Updaters.CollectionChanged += UpdatersOnCollectionChanged;
88  EnableSorting = true;
89  }
90 
91  /// <summary>
92  /// Gets or sets the maximum particle count for all updaters, must be power of 2.
93  /// </summary>
94  /// <value>The capacity count.</value>
95  /// <remarks>
96  /// The sum of ParticlesUpdaters.CapacityCount must be lower than this value.
97  /// </remarks>
98  public int CapacityCount
99  {
100  get
101  {
102  return capacityCount;
103  }
104  set
105  {
106  if (ParticleUtils.CalculateMaximumPowerOf2Count(value) != value)
107  throw new ArgumentException("CapacityCount must be a power of 2");
108 
109  if (value < updatersCount)
110  throw new ArgumentException(string.Format("Cannot change Maximum Count [{0}] to be lower than the sum of Updaters.MaximumCount [{1}]", value,
111  updatersCount));
112 
113  capacityCount = value;
114  }
115  }
116 
117  public int StructureSize { get; set; }
118 
119  public string StructureName { get; set; }
120 
121  public ShaderClassSource RenderShader { get; set; }
122 
123  /// <summary>
124  /// Gets or sets a value indicating whether the sort is enabled (true by default).
125  /// </summary>
126  /// <value><c>true</c> if [sort is enabled]; otherwise, <c>false</c>.</value>
127  public bool EnableSorting { get; set; }
128 
129  public MainPlugin MainPlugin { get; set; }
130 
131  public RenderTargetsPlugin MainTargetPlugin { get; set; }
132 
133  /// <summary>
134  /// Gets the particle updaters.
135  /// </summary>
136  /// <value>The particle updaters.</value>
137  public TrackingCollection<ParticleEmitterComponent> Updaters { get; private set; }
138 
139  public override void Initialize()
140  {
141  base.Initialize();
142 
143  graphicsDeviceService = Services.GetSafeServiceAs<IGraphicsDeviceService>();
144 
145  // Create and register render passes
146  updatePasses = new RenderPass("Update");
147  copyPass = new RenderPass("Copy");
148  sortPass = new RenderPass("Sort");
149 
150  bitonicSort1Pass1 = new RenderPass("BitonicSort1Pass1");
151  bitonicSort1Pass2 = new RenderPass("BitonicSort1Pass2");
152  bitonicSort2Passes = new RenderPass[(int)Math.Log(MaximumDepthLevel, 2) - 1];
153  effectBitonicSort2 = new EffectOld[bitonicSort2Passes.Length];
154 
155  renderPass = new RenderPass("Render");
156  }
157 
158  public override void Load()
159  {
160  base.Load();
161 
162  if (string.IsNullOrEmpty(StructureName) || StructureSize == 0)
163  throw new InvalidOperationException("StructureName and StructureSize must be setup on ParticlePlugin");
164 
165  // Add passes to the render pass
166  RenderPass.AddPass(updatePasses, copyPass, sortPass, renderPass);
167 
168  // Create effect to copy from particle buffer to sort buffer
169  effectCopyToSortBuffer = this.EffectSystemOld.BuildEffect("CopyToSortBuffer").Using(
170  new ComputeShaderPlugin(new ShaderClassSource("ParticleSortInitializer"), MaximumThreadPerGroup, 1, 1)
171  {
172  RenderPass = copyPass,
173  Macros = { new ShaderMacro("PARTICLE_STRUCT", StructureName) }
174  });
175  effectCopyToSortBuffer.KeepAliveBy(ActiveObjects);
176  effectCopyToSortBuffer.Parameters.AddSources(MainPlugin.ViewParameters);
177  effectMeshCopyToSortBuffer = new EffectMesh(effectCopyToSortBuffer);
178 
179  // Clear the sort buffer
180  copyPass.StartPass.AddFirst = (context) =>
181  {
182  if (CapacityCount > 0)
183  {
184  // TODO handle progressive sorting
185  context.GraphicsDevice.ClearReadWrite(sortBuffer, new UInt4(0xFF7FFFFF)); // 0xFF7FFFFF = - float.MaxValue
186  }
187  };
188 
189  // Create effect for bitonic sort 1 - pass 1
190  effectBitonicSort1Pass1 = this.EffectSystemOld.BuildEffect("ParticleBitonicSort1-Pass1").Using(
191  new ComputeShaderPlugin(new ShaderClassSource("ParticleBitonicSort1"), MaximumThreadPerGroup, 1, 1)
192  {
193  RenderPass = bitonicSort1Pass1,
194  Macros = { new ShaderMacro("PARTICLE_STRUCT", StructureName),
195  new ShaderMacro("PARTICLE_SORT_PASS", 0)
196  }
197  });
198  effectBitonicSort1Pass1.KeepAliveBy(this);
199  effectMeshSort1Pass1 = new EffectMesh(effectBitonicSort1Pass1);
200 
201  // Create effect for bitonic sort 1 - pass 2
202  effectBitonicSort1Pass2 = this.EffectSystemOld.BuildEffect("ParticleBitonicSort1-Pass2").Using(
203  new ComputeShaderPlugin(new ShaderClassSource("ParticleBitonicSort1"), MaximumThreadPerGroup, 1, 1)
204  {
205  RenderPass = bitonicSort1Pass2,
206  Macros = { new ShaderMacro("PARTICLE_STRUCT", StructureName),
207  new ShaderMacro("PARTICLE_SORT_PASS", 1)
208  }
209  });
210  effectBitonicSort1Pass2.KeepAliveBy(this);
211  effectMeshSort1Pass2 = new EffectMesh(effectBitonicSort1Pass2);
212 
213  // Creates Effect for bitonic sort 2
214  var currentDepth = MaximumDepthLevel;
215  for (int i = 0; i < bitonicSort2Passes.Length; i++)
216  {
217  var bitonicShader = new RenderPass(string.Format("Bitonic-{0}", currentDepth));
218  bitonicSort2Passes[i] = bitonicShader;
219 
220  // Compile the new shader for this count
221  effectBitonicSort2[i] = this.EffectSystemOld.BuildEffect("ParticleBitonicSort2-" + currentDepth).Using(
222  new ComputeShaderPlugin(new ShaderClassSource("ParticleBitonicSort2", currentDepth), MaximumThreadPerGroup, 1, 1)
223  {
224  RenderPass = bitonicShader,
225  Macros = { new ShaderMacro("PARTICLE_STRUCT", StructureName) }
226  });
227  effectBitonicSort2[i].KeepAliveBy(this);
228 
229  currentDepth /= 2;
230  }
231  effectMeshBitonicSort2 = new EffectMesh[bitonicSort2Passes.Length];
232  for (int i = 0; i < effectMeshBitonicSort2.Length; i++)
233  effectMeshBitonicSort2[i] = new EffectMesh(effectBitonicSort2[i]);
234 
235  // Creates Effect for rendering
236  EffectOld particleRenderEffect = this.EffectSystemOld.BuildEffect("ParticleRender")
237  .Using(new StateShaderPlugin() { UseBlendState = true, UseDepthStencilState = true, RenderPass = renderPass })
238  .Using(new BasicShaderPlugin(RenderShader)
239  {
240  RenderPass = renderPass,
241  Macros = { new ShaderMacro("PARTICLE_STRUCT", StructureName) }
242  });
243  particleRenderEffect.KeepAliveBy(this);
244 
245  particleRenderEffect.Parameters.AddSources(this.Parameters);
246  particleRenderEffect.Parameters.AddSources(MainPlugin.ViewParameters);
247  particleRenderEffect.Parameters.Set(EffectPlugin.BlendStateKey, graphicsDeviceService.GraphicsDevice.BlendStates.AlphaBlend);
248  particleRenderEffect.Parameters.Set(EffectPlugin.DepthStencilStateKey, MainTargetPlugin.DepthStencilState);
249  particleRenderEffect.Parameters.Set(RenderTargetKeys.DepthStencilSource, MainTargetPlugin.DepthStencil.Texture);
250 
251  effectMeshRender = new EffectMesh(particleRenderEffect);
252 
253  effectMeshRender.Render += (context) =>
254  {
255  if (currentParticleCount > 0)
256  {
257  context.GraphicsDevice.SetVertexArrayObject(null);
258  context.GraphicsDevice.SetViewport(MainTargetPlugin.Viewport);
259  //context.GraphicsDevice.SetRenderTargets(MainTargetPlugin.DepthStencil, 0, MainTargetPlugin.RenderTarget);
260 
261  context.GraphicsDevice.SetRenderTargets((MainTargetPlugin.DepthStencilReadOnly != null) ? MainTargetPlugin.DepthStencilReadOnly : MainTargetPlugin.DepthStencil, RenderTarget);
262 
263  // TODO HANDLE dynamic count
264  //context.GraphicsDevice.Draw(PrimitiveType.PointList, CapacityCount);
265  context.GraphicsDevice.Draw(PrimitiveType.PointList, currentParticleCount);
266  }
267  //context.GraphicsDevice.SetRenderTargets(null, 0, new ITexture2D[] { null } );
268  };
269 
270  if (OfflineCompilation)
271  return;
272 
273  // Allocate global buffers at register time
274  // Buffers can also be reallocated at runtime
275  OnCapacityCountChange();
276 
277  // Add our local meshes to render RenderContext.RenderPassEnumerators
278  RenderSystem.RenderPassEnumerators.Add(meshesToRender);
279 
280  // Register update per frame
281  RenderSystem.GlobalPass.StartPass += OnFrameUpdate;
282 
283  // Register sort pass
284  sortPass.EndPass.Set = ComputeBitonicSort;
285  }
286 
287  public override void Unload()
288  {
289  if (!OfflineCompilation)
290  {
291 
292  }
293 
294  base.Unload();
295  }
296 
297  /// <summary>
298  /// Allocates global buffers.
299  /// </summary>
300  private void OnCapacityCountChange()
301  {
302  // Allocate global buffers
303  bool allocateGlobalBuffer = false;
304  if (globalBuffer == null)
305  {
306  allocateGlobalBuffer = true;
307  }
308  else if (globalBuffer.ElementCount != CapacityCount)
309  {
310  // Reallocate global buffer if capacity changed
311  allocateGlobalBuffer = true;
312  // Release previous buffer
313  globalBuffer.Release();
314  sortBuffer.Release();
315  }
316 
317  // Allocate new buffers
318  if (allocateGlobalBuffer)
319  {
320  if (CapacityCount > 0)
321  {
322  globalBuffer = Buffer.Structured.New(graphicsDeviceService.GraphicsDevice, CapacityCount, StructureSize, true);
323  globalBuffer.Name = "ParticleGlobalBuffer";
324  sortBuffer = Buffer.Structured.New(graphicsDeviceService.GraphicsDevice, CapacityCount, sizeof(int) * 2, true);
325  sortBuffer.Name = "ParticleSortBuffer";
326  }
327  }
328  }
329 
330  private static void InvokeMeshPass(EffectMesh mesh, ThreadContext context, bool skipEffectPass = false)
331  {
332  var effectPass = mesh.EffectPass;
333 
334  // Execute EffectPass - StartPass
335  if (!skipEffectPass)
336  effectPass.StartPass.Invoke(context);
337 
338  // Execute MeshPass - StartPass - EndPass
339  context.EffectMesh = mesh;
340  mesh.Render.Invoke(context);
341  context.EffectMesh = null;
342 
343  // Execute EffectPass - EndPass
344  if (!skipEffectPass)
345  effectPass.EndPass.Invoke(context);
346  }
347 
348  private void OnFrameUpdate(ThreadContext context)
349  {
350  updatePasses.Enabled = true; //!RenderContext.IsPaused;
351 
352  // Add new updater to the current list
353  foreach (var particleUpdater in Updaters)
354  {
355  ParticleUpdaterState particleUpdaterState = null;
356  foreach (ParticleUpdaterState state in currentUpdaters)
357  {
358  if (ReferenceEquals(state.UserState, particleUpdater))
359  {
360  particleUpdaterState = state;
361  break;
362  }
363  }
364  if (particleUpdaterState == null)
365  currentUpdaters.Add(new ParticleUpdaterState() { UserState = particleUpdater, StructureSize = StructureSize});
366  }
367 
368  // Get updater to remove from current list
369  foreach (var particleUpdater in currentUpdaters)
370  {
371  ParticleEmitterComponent particleUpdaterState = null;
372  foreach (ParticleEmitterComponent state in Updaters)
373  {
374  if (ReferenceEquals(state, particleUpdater.UserState))
375  {
376  particleUpdaterState = state;
377  break;
378  }
379  }
380  if (particleUpdaterState == null)
381  updatersToRemove.Add(particleUpdater);
382  }
383 
384  // Remove from the current list
385  foreach (var particleUpdaterState in updatersToRemove)
386  {
387  currentUpdaters.Remove(particleUpdaterState);
388 
389  // Remove the mesh to be rendered
390  meshesToRender.RemoveMesh(particleUpdaterState.MeshUpdater);
391 
392  // Dispose the previous particule updater as it is no longer used
393  particleUpdaterState.DisposeBuffers();
394  }
395 
396  // Reallocate global buffers if needed
397  if (updatersCount > capacityCount)
398  {
399  CapacityCount = updatersCount;
400  OnCapacityCountChange();
401  }
402 
403  int particleUpdaterIndex = 1;
404 
405  currentParticleCount = 0;
406  // Update start index into global buffers for all CPU/GPU static buffers.
407  int startIndex = 0;
408  for (int i = 0; i < currentUpdaters.Count; i++)
409  {
410  var currentState = currentUpdaters[i];
411  var userState = currentState.UserState;
412 
413  if (!userState.IsDynamicEmitter)
414  {
415  // If there is a change from dynamic type to CPU/GPU static buffer, we need to deallocate previous Append/ConsumeBuffers
416  if (currentState.IsDynamicEmitter)
417  currentState.DisposeBuffers();
418  }
419  else if (userState.Count > currentState.Count)
420  {
421  // This is a dynamic buffer and the new buffer is larger than previous one
422  // or we simply need to allocate new consume/append buffers
423  currentState.AllocateBuffers(context.GraphicsDevice);
424  }
425 
426  // Create Effect shader
427  if (!ReferenceEquals(userState.Shader, currentState.Shader) || currentState.Count != userState.Count)
428  {
429  // Remove the effect pass
430  updatePasses.RemovePass(currentState.EffectPass);
431 
432  // Dispose previous effects
433  currentState.DisposeEffects();
434 
435  // Remove mesh
436  if (currentState.MeshUpdater != null)
437  {
438  meshesToRender.RemoveMesh(currentState.MeshUpdater);
439  currentState.MeshUpdater = null;
440  }
441 
442  // Compile new shader
443  if (userState.Shader != null)
444  {
445  string name = userState.Name ?? string.Format("{0}{1}", userState.Shader.ClassName, particleUpdaterIndex++);
446 
447  currentState.EffectPass = new RenderPass(name);
448  updatePasses.AddPass(currentState.EffectPass);
449 
450  // Calculate the best dispatch thread count
451  int dispatchCount = MaximumThreadPerGroup;
452  while (dispatchCount > 0)
453  {
454  // If the maximum count is a multiple of the current dispatchCount use it
455  if ((userState.Count & (dispatchCount - 1)) == 0)
456  break;
457  dispatchCount >>= 1;
458  }
459 
460  // Compile the new shader for this count
461  currentState.EffectUpdater = this.EffectSystemOld.BuildEffect(name).Using(
462  new ComputeShaderPlugin(userState.Shader, dispatchCount, 1, 1)
463  {
464  RenderPass = currentState.EffectPass,
465  Macros = { new ShaderMacro("PARTICLE_STRUCT", StructureName) }
466  });
467 
468  // Instantiate the mesh updater
469  var meshParams = new ParameterCollection(name);
470  currentState.MeshUpdater = new EffectMesh(currentState.EffectUpdater, new Mesh { Parameters = meshParams }).Dispatch(userState.Count / dispatchCount, 1, 1);
471  currentState.MeshUpdater.Parameters.AddSources(Parameters);
472  currentState.MeshUpdater.Parameters.AddSources(MainPlugin.ViewParameters);
473  currentState.MeshUpdater.Parameters.AddSources(userState.Parameters);
474 
475  // Setup Append/Consume
476  if (userState.IsDynamicEmitter)
477  {
478  currentState.MeshUpdater.Parameters.Set(ParticleBaseKeys.ParticleInputBuffer, currentState.ConsumeBuffer);
479  currentState.MeshUpdater.Parameters.Set(ParticleBaseKeys.ParticleOutputBuffer, currentState.AppendBuffer);
480  currentState.MeshUpdater.Parameters.Set(ParticleBaseKeys.ParticleStartIndex, (uint)0);
481  }
482  else
483  {
484  // Setup update on global buffer
485  currentState.MeshUpdater.Parameters.Set(ParticleBaseKeys.ParticleGlobalBuffer, globalBuffer);
486  currentState.MeshUpdater.Parameters.Set(ParticleBaseKeys.ParticleStartIndex, (uint)startIndex);
487  }
488 
489  // Add this updater to the rendering list
490  meshesToRender.AddMesh(currentState.MeshUpdater);
491  }
492  }
493 
494  // Setup Append/Consume
495  if (currentState.MeshUpdater != null && !userState.IsDynamicEmitter && currentState.StartIndex != startIndex)
496  {
497  // Setup update on global buffer
498  currentState.MeshUpdater.Parameters.Set(ParticleBaseKeys.ParticleStartIndex, (uint)startIndex);
499  }
500 
501  // Transfer CPU buffer to GPU
502  if (!ReferenceEquals(userState.ParticleData, currentState.ParticleData)
503  || userState.UpdateNextBuffer
504  || userState.Type == ParticleEmitterType.CpuDynamic
505  || currentState.StartIndex != startIndex
506  || currentState.Count != userState.Count
507  )
508  {
509  // Update the data if necessary
510  userState.OnUpdateData();
511 
512  if (userState.ParticleData != null)
513  {
514  var handle = GCHandle.Alloc(userState.ParticleData, GCHandleType.Pinned);
515  globalBuffer.SetData(context.GraphicsDevice, new DataPointer(handle.AddrOfPinnedObject(), userState.Count * StructureSize), startIndex * StructureSize);
516  handle.Free();
517  }
518  userState.UpdateNextBuffer = false;
519  }
520 
521  // Replicate the shader to the current state
522  currentState.Type = userState.Type;
523  currentState.Shader = userState.Shader;
524  currentState.Count = userState.Count;
525  currentState.ParticleElementSize = userState.ParticleElementSize;
526  currentState.ParticleData = userState.ParticleData;
527  currentState.StartIndex = startIndex;
528 
529  // Copy Maximum count to current state
530  currentParticleCount += userState.Count;
531 
532  // Update start index
533  startIndex += userState.Count;
534  }
535 
536  // Add mesh to the render list
537  if (updatersCount > 0)
538  {
539  if (!isParticleRenderingEnabled)
540  {
541  isParticleRenderingEnabled = true;
542 
543  meshesToRender.AddMesh(effectMeshRender);
544  meshesToRender.AddMesh(effectMeshCopyToSortBuffer);
545  }
546  }
547  else
548  {
549  isParticleRenderingEnabled = false;
550  meshesToRender.RemoveMesh(effectMeshRender);
551  meshesToRender.RemoveMesh(effectMeshCopyToSortBuffer);
552  }
553 
554  // Prepare mesh for rendering
555  PrepareForRendering();
556  }
557 
558  private bool isParticleRenderingEnabled;
559 
560 
561 
562  private void PrepareForRendering()
563  {
564  effectMeshCopyToSortBuffer.Parameters.Set(ComputeShaderPlugin.ThreadGroupCount, new Int3(currentParticleCount / MaximumThreadPerGroup, 1, 1));
565  effectMeshCopyToSortBuffer.Parameters.Set(ParticleBaseKeys.ParticleCount, (uint)currentParticleCount);
566 
567  effectMeshCopyToSortBuffer.Parameters.Set(ParticleBaseKeys.ParticleSortBuffer, sortBuffer);
568  effectMeshCopyToSortBuffer.Parameters.Set(ParticleBaseKeys.ParticleGlobalBuffer, globalBuffer);
569 
570  effectMeshRender.Parameters.Set(ParticleBaseKeys.ParticleSortBuffer, sortBuffer);
571  effectMeshRender.Parameters.Set(ParticleBaseKeys.ParticleGlobalBuffer, globalBuffer);
572  effectMeshRender.Parameters.Set(ParticleBaseKeys.ParticleCount, (uint)currentParticleCount);
573  }
574 
575  /// <summary>
576  /// Computes a bitonic sort on the sort buffer.
577  /// </summary>
578  /// <param name="context">The context.</param>
579  /// <remarks>
580  /// The sort buffer is a structure containing:
581  /// struct SortData { uint ParticleIndex; float DepthValue; };
582  /// This methods reorders the sort buffer based on the depth value, from lowest to highest.
583  /// </remarks>
584  private void ComputeBitonicSort(ThreadContext context)
585  {
586  if (!EnableSorting || !isParticleRenderingEnabled)
587  return;
588 
589  // TODO: handle progressive sorting when no dynamic emitter
590 
591  int particleCount = updatersCount;
592 
593  // Precalculates values from CapacityCount, MaximumDepthLevel and MaximumThreadPerGroup
594  var depthLevelCount = (int)Math.Log(particleCount, 2);
595  var optimLevelStart = (int)Math.Log(MaximumDepthLevel, 2);
596  int blockSize = 1;
597  int numberOfGroupForOptim = particleCount / MaximumThreadPerGroup;
598  int numberOfGroups = numberOfGroupForOptim / 2;
599 
600  for (int i = 0; i < depthLevelCount; i++, blockSize *= 2)
601  {
602  // Create dispatch pass for bitonic step1
603  effectMeshSort1Pass1.Parameters.Set(ParticleBaseKeys.ParticleStartIndex, (uint)blockSize);
604  effectMeshSort1Pass1.Parameters.Set(ParticleBaseKeys.ParticleSortBuffer, sortBuffer);
605  effectMeshSort1Pass1.Parameters.Set(ComputeShaderPlugin.ThreadGroupCount, new Int3(numberOfGroups, 1, 1));
606 
607  // Invoke Mesh Pass
608  InvokeMeshPass(effectMeshSort1Pass1, context);
609 
610  // Setup multiple Pass2
611  var subOffset = blockSize;
612 
613  for (int j = 0; j < i; j++, subOffset /= 2)
614  {
615  // Use optimized group version if possible
616  int k = optimLevelStart - i + j;
617  if (k >= 0 && k < effectMeshBitonicSort2.Length)
618  {
619  var subMesh = effectMeshBitonicSort2[k];
620 
621  subMesh.Parameters.Set(ParticleBaseKeys.ParticleSortBuffer, sortBuffer);
622  subMesh.Parameters.Set(ComputeShaderPlugin.ThreadGroupCount, new Int3(numberOfGroupForOptim, 1, 1));
623 
624  InvokeMeshPass(subMesh, context);
625  break;
626  }
627 
628  // Else use standard version
629  effectMeshSort1Pass2.Parameters.Set(ParticleBaseKeys.ParticleStartIndex, (uint)(subOffset / 2));
630  effectMeshSort1Pass2.Parameters.Set(ParticleBaseKeys.ParticleSortBuffer, sortBuffer);
631  effectMeshSort1Pass2.Parameters.Set(ComputeShaderPlugin.ThreadGroupCount, new Int3(numberOfGroups, 1, 1));
632 
633  // When j > 0, we can don't need to reapply the effect pass
634  InvokeMeshPass(effectMeshSort1Pass2, context, j > 0);
635  }
636  }
637  }
638 
639  private void UpdatersOnCollectionChanged(object sender, TrackingCollectionChangedEventArgs trackingCollectionChangedEventArgs)
640  {
641  updatersCount = ParticleUtils.CalculateMaximumPowerOf2Count(Updaters.Sum(particleUpdater => particleUpdater.Count));
642  }
643 
644  private class ParticleUpdaterState : ParticleEmitterComponent
645  {
646  public ParticleEmitterComponent UserState;
647 
648  public Buffer AppendBuffer;
649 
650  public Buffer ConsumeBuffer;
651 
652  public RenderPass EffectPass;
653 
654  public EffectOld EffectUpdater;
655 
656  public EffectMesh MeshUpdater;
657 
658  public int StartIndex;
659 
660  public int StructureSize;
661 
662 
663  /// <summary>
664  /// Allocates the buffers.
665  /// </summary>
666  public void AllocateBuffers(GraphicsDevice graphicsDevice)
667  {
668  DisposeBuffers();
669  AppendBuffer = Buffer.StructuredAppend.New(graphicsDevice, UserState.Count, StructureSize);
670  ConsumeBuffer = Buffer.StructuredAppend.New(graphicsDevice, UserState.Count, StructureSize);
671  }
672 
673  public void DisposeEffects()
674  {
675  if (EffectUpdater != null)
676  {
677  EffectUpdater.Release();
678  EffectUpdater = null;
679  }
680  }
681 
682  public void DisposeBuffers()
683  {
684  if (AppendBuffer != null)
685  {
686  AppendBuffer.Release();
687  AppendBuffer = null;
688  }
689 
690  if (ConsumeBuffer != null)
691  {
692  ConsumeBuffer.Release();
693  ConsumeBuffer = null;
694  }
695  }
696  }
697 
698  public RenderTarget RenderTarget { get; set; }
699  }
700 }
SiliconStudio.Paradox.Shaders.ShaderMacro ShaderMacro
ParticlePlugin(string name)
Initializes a new instance of the ParticlePlugin class.
Service providing method to access GraphicsDevice life-cycle.
SiliconStudio.Paradox.Graphics.Buffer Buffer
ParticlePlugin()
Initializes a new instance of the ParticlePlugin class.
Basic shader plugin built directly from shader source file.
Represents a three dimensional mathematical vector.
Definition: Int3.cs:41
All-in-One Buffer class linked SharpDX.Direct3D11.Buffer.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
Plugin used for the main rendering view.
Definition: MainPlugin.cs:15
static int CalculateMaximumPowerOf2Count(int value)
Definition: ParticleUtils.cs:7
Level10 render pass using a depth buffer and a render target.
Represents a four dimensional mathematical vector.
Definition: UInt4.cs:35
RenderPass is a hierarchy that defines how to collect and render meshes.
Definition: RenderPass.cs:19