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
        }