internal static int Gather(MyGPUEmitterData[] data, out SharpDX.Direct3D11.ShaderResourceView 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 (emitter.TextureId != Resources.TexId.NULL) { MyRenderProxy.Assert(m_textureArrayIndices.ContainsKey(emitter.TextureId)); textureIndex = m_textureArrayIndices[emitter.TextureId].Index; } else textureIndex = 0; int bufferIndex = emitter.BufferIndex; data[bufferIndex] = emitter.GPUEmitter.Data; Vector3 pos = emitter.GPUEmitter.WorldPosition - MyRender11.Environment.CameraPosition; data[bufferIndex].RotationMatrix.M14 = pos.X; data[bufferIndex].RotationMatrix.M24 = pos.Y; data[bufferIndex].RotationMatrix.M34 = pos.Z; data[bufferIndex].TextureIndex1 |= textureIndex << (ATLAS_INDEX_BITS + ATLAS_DIMENSION_BITS * 2); if (bufferIndex > maxEmitterIndex) maxEmitterIndex = bufferIndex; } } /*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(); if (m_textureArray != null) textureArraySRV = m_textureArray.SRV; else textureArraySRV = null; // 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; }
// Per-frame emission of particles into the GPU simulation private static void Emit(int emitterCount, MyGPUEmitterData[] emitterData) { // update emitter data var mapping = MyMapping.MapDiscard(m_emitterStructuredBuffer.Buffer); 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.BindUAV(0, m_particleBuffer); RC.BindUAV(1, m_deadListBuffer); RC.BindUAV(2, m_skippedParticleCountBuffer, 0); RC.CSSetCB(1, m_emitterConstantBuffer); RC.CSBindRawSRV(0, Resources.MyTextures.RandomTexId); RC.CSBindRawSRV(1, m_emitterStructuredBuffer); RC.SetCS(m_csEmit); RC.DeviceContext.Dispatch(numThreadGroupsX, numThreadGroupsY, 1); RC.CSSetCB(1, null); RC.SetCS(m_csEmitSkipFix); // Disaptch a set of 1d thread groups to fill out the dead list, one thread per particle RC.DeviceContext.Dispatch(1, 1, 1); } #if DEBUG //m_numDeadParticlesAfterEmit = ReadCounter(m_deadListBuffer); #endif }