// Per-frame emission of particles into the GPU simulation private static void Emit(int emitterCount, MyGPUEmitterData[] emitterData, ISrvBindable depthRead) { // update emitter data var mapping = MyMapping.MapDiscard(m_emitterStructuredBuffer); int maxParticlesToEmitThisFrame = 0; for (int i = 0; i < emitterCount; i++) { mapping.WriteAndPosition(ref emitterData[i]); if (emitterData[i].NumParticlesToEmitThisFrame > maxParticlesToEmitThisFrame) maxParticlesToEmitThisFrame = emitterData[i].NumParticlesToEmitThisFrame; } mapping.Unmap(); int numThreadGroupsX = align(maxParticlesToEmitThisFrame, MAX_PARTICLE_EMIT_THREADS) / MAX_PARTICLE_EMIT_THREADS; int numThreadGroupsY = align(emitterCount, MAX_EMITTERS) / MAX_EMITTERS; // update emitter count mapping = MyMapping.MapDiscard(m_emitterConstantBuffer); mapping.WriteAndPosition(ref emitterCount); mapping.WriteAndPosition(ref numThreadGroupsX); mapping.Unmap(); if (maxParticlesToEmitThisFrame > 0) { // Set resources but don't reset any atomic counters RC.ComputeShader.SetUav(0, m_particleBuffer); RC.ComputeShader.SetUav(1, m_deadListBuffer); RC.ComputeShader.SetUav(2, m_skippedParticleCountBuffer, 0); RC.ComputeShader.SetConstantBuffer(1, m_emitterConstantBuffer); RC.ComputeShader.SetSrv(0, depthRead); RC.ComputeShader.SetSrv(1, m_emitterStructuredBuffer); RC.ComputeShader.Set(m_csEmit); RC.Dispatch(numThreadGroupsX, numThreadGroupsY, 1); RC.ComputeShader.SetConstantBuffer(1, null); RC.ComputeShader.Set(m_csEmitSkipFix); // Disaptch a set of 1d thread groups to fill out the dead list, one thread per particle RC.Dispatch(1, 1, 1); } }
internal static int Gather(MyGPUEmitterData[] data, out ISrvBindable textureArraySrv) { for (int i = 0; i < MAX_LIVE_EMITTERS; i++) { data[i].NumParticlesToEmitThisFrame = 0; } // sort emitters! List<MyLiveData> emitters = m_emitters.Values.ToList(); //if (emitters.Count > MAX_LIVE_EMITTERS) emitters.Sort(); int maxEmitterIndex = -1; uint textureIndex = 0; int unassociatedCount = 0; int skipCount = 0; int unsortedCount = 0; int firstUnassociatedIndex = -1; int lastAssociatedIndex = -1; for (int i = 0; i < emitters.Count; i++) { var emitter = emitters[i]; // assiociate buffer index to new emitters & track overload to free space for unassociated near emitters later if (emitter.BufferIndex == -1) { if (m_freeBufferIndices.Count > 0) { emitter.BufferIndex = m_freeBufferIndices.Pop(); } else { unassociatedCount++; if (firstUnassociatedIndex == -1) firstUnassociatedIndex = i; } } else { skipCount = unassociatedCount; lastAssociatedIndex = i; if (unassociatedCount > 0) unsortedCount++; } if (MyCommon.TimerMs > emitter.DieAt) emitter.GPUEmitter.Data.Flags |= GPUEmitterFlags.Dead; if (emitter.BufferIndex != -1) { if ((emitter.GPUEmitter.Data.Flags & GPUEmitterFlags.FreezeEmit) == 0) { float toEmit = MyCommon.LastFrameDelta() * emitter.GPUEmitter.ParticlesPerSecond + emitter.ParticlesEmittedFraction; emitter.GPUEmitter.Data.NumParticlesToEmitThisFrame = (int)toEmit; emitter.ParticlesEmittedFraction = toEmit - emitter.GPUEmitter.Data.NumParticlesToEmitThisFrame; } if (string.IsNullOrEmpty(emitter.GPUEmitter.AtlasTexture)) { MyRenderProxy.Assert(m_textureArrayIndices.ContainsKey(emitter.GPUEmitter.AtlasTexture)); textureIndex = m_textureArrayIndices[emitter.GPUEmitter.AtlasTexture].Index; } else textureIndex = 0; int bufferIndex = emitter.BufferIndex; data[bufferIndex] = emitter.GPUEmitter.Data; Vector3 pos = emitter.GPUEmitter.WorldPosition - MyRender11.Environment.Matrices.CameraPosition; data[bufferIndex].RotationMatrix.M14 = pos.X; data[bufferIndex].RotationMatrix.M24 = pos.Y; data[bufferIndex].RotationMatrix.M34 = pos.Z; data[bufferIndex].PositionDelta = emitter.LastWorldPosition - emitter.GPUEmitter.WorldPosition; data[bufferIndex].TextureIndex1 |= textureIndex << (ATLAS_INDEX_BITS + ATLAS_DIMENSION_BITS * 2); if (bufferIndex > maxEmitterIndex) maxEmitterIndex = bufferIndex; } emitter.LastWorldPosition = emitter.GPUEmitter.WorldPosition; } /*MyRenderStats.Generic.WriteFormat("GPU particles allocated: {0}", totalParticles, VRage.Stats.MyStatTypeEnum.CurrentValue, 300, 0); MyRenderStats.Generic.WriteFormat("GPU particles overload: {0}", overloadedParticles ? 1.0f : 0, VRage.Stats.MyStatTypeEnum.CurrentValue, 300, 0); MyRenderStats.Generic.WriteFormat("GPU emitters overload: {0}", overloadedEmitters ? 1.0f : 0, VRage.Stats.MyStatTypeEnum.CurrentValue, 300, 0);*/ UpdateTextureArray(); textureArraySrv = m_textureArray; // stop emitters far away to make room for emitters nearby if (skipCount > 0 && unsortedCount > 0) { // iterate until buffer-unassociated index is larger then unassocited one for (int i = firstUnassociatedIndex, j = lastAssociatedIndex; i < j; ) { var emitter = emitters[j]; // free last buffer-associated emitter data[emitter.BufferIndex].Flags |= GPUEmitterFlags.Dead; data[emitter.BufferIndex].NumParticlesToEmitThisFrame = 0; m_freeBufferIndices.Push(emitter.BufferIndex); emitter.BufferIndex = -1; // find new last buffer-associated emitter do { j--; } while (j > 0 && emitters[j].BufferIndex == -1); // find next buffer-unassociated emitter do { i++; } while (i < emitters.Count && emitters[i].BufferIndex != -1); } } foreach (var emitter in emitters) if ((emitter.GPUEmitter.Data.Flags & GPUEmitterFlags.Dead) > 0) { Remove(emitter); } return maxEmitterIndex + 1; }