Beispiel #1
0
        public static void Start(Args args)
        {
            var job = m_instancePool.Allocate();

            job.m_args = args;
            Parallel.Start(job, job.m_onComplete);
        }
Beispiel #2
0
        public static void Start(Args args)
        {
            var job = m_instancePool.Allocate();

            job.m_args = args;
            args.Tracker.Add(args.DefId, job);

            MyPrecalcComponent.EnqueueBack(job, true);
        }
        public static void Start(MyVoxelPhysicsBody targetPhysics, ref HashSet <Vector3I> cellBatchForSwap)
        {
            var job = m_instancePool.Allocate();

            job.m_targetPhysics = targetPhysics;
            MyUtils.Swap(ref job.CellBatch, ref cellBatchForSwap);
            Debug.Assert(targetPhysics.RunningBatchTask == null);
            targetPhysics.RunningBatchTask = job;
            MyPrecalcComponent.EnqueueBack(job, true);
        }
Beispiel #4
0
        public static void Start(MyVoxelPhysicsBody targetPhysics, ref HashSet <Vector3I> cellBatchForSwap, int lod)
        {
            MyPrecalcJobPhysicsBatch job = m_instancePool.Allocate();

            job.Lod             = lod;
            job.m_targetPhysics = targetPhysics;
            MyUtils.Swap <HashSet <Vector3I> >(ref job.CellBatch, ref cellBatchForSwap);
            targetPhysics.RunningBatchTask[lod] = job;
            MyPrecalcComponent.EnqueueBack(job);
        }
        public static void Start(Args args)
        {
            var job = m_instancePool.Allocate();

            job.m_args = args;
            args.Tracker.Cancel(args.GeometryCell);
            args.Tracker.Add(args.GeometryCell, job);

            MyPrecalcComponent.EnqueueBack(job);
        }
        public static void Start(Args args)
        {
            Debug.Assert(args.Storage != null);
            var job = m_instancePool.Allocate();

            job.m_isCancelled = false;
            job.m_args        = args;
            args.RenderWorkTracker.Add(args.WorkId, job);

            MyPrecalcComponent.EnqueueBack(job, false /*job.m_args.IsHighPriority*/);
        }
Beispiel #7
0
        public void SpawnFlora(Vector3D pos)
        {
            if (m_planetEnvironmentSectors == null)
            {
                m_planetEnvironmentSectors = new Dictionary <Vector3I, MyPlanetEnvironmentSector>(500);
            }

            Vector3D gravity       = GetWorldGravityNormalized(ref pos);
            Vector3D perpedincular = MyUtils.GetRandomPerpendicularVector(ref gravity);
            Vector3D third         = Vector3D.Cross(gravity, perpedincular);

            perpedincular += third;

            Vector3I min = new Vector3I(-ENVIROMENT_EXTEND);
            Vector3I max = new Vector3I(ENVIROMENT_EXTEND);

            Vector3 offset = new Vector3(-MyPlanetEnvironmentSector.SECTOR_SIZE_METERS);

            for (var it = new Vector3I.RangeIterator(ref min, ref max); it.IsValid(); it.MoveNext())
            {
                Vector3D currentPos = pos + it.Current * offset * perpedincular;
                currentPos = PlaceToOrbit(currentPos, ref gravity);

                if (false == ChekPosition(currentPos))
                {
                    Vector3I newSector = Vector3I.Floor(currentPos / MyPlanetEnvironmentSector.SECTOR_SIZE_METERS);
                    if (m_planetSectorsPool == null)
                    {
                        m_planetSectorsPool = new MyDynamicObjectPool <MyPlanetEnvironmentSector>(400);
                    }


                    MyPlanetEnvironmentSector sector = m_planetSectorsPool.Allocate();

                    sector.Init(ref newSector, this);
                    m_planetEnvironmentSectors[newSector] = sector;
                    sector.PlaceItems();
                }
            }

            Vector3I sectorCoords = Vector3I.Floor(PlaceToOrbit(pos, ref gravity) / MyPlanetEnvironmentSector.SECTOR_SIZE_METERS);

            Vector3I keepMin = sectorCoords + new Vector3I(-ENVIROMENT_EXTEND_KEEP);
            Vector3I keepMax = sectorCoords + new Vector3I(ENVIROMENT_EXTEND_KEEP);

            foreach (var enviromentSector in m_planetEnvironmentSectors)
            {
                if (enviromentSector.Key.IsInsideInclusive(keepMin, keepMax))
                {
                    m_sectorsToKeep.Add(enviromentSector.Key);
                }
            }
        }
Beispiel #8
0
        public static void Start(Args args)
        {
            Debug.Assert(args.Storage != null);
            var job = m_instancePool.Allocate();

            job.m_isCancelled = false;
            job.m_args        = args;

            if (!args.RenderWorkTracker.Exists(args.WorkId))
            {
                args.RenderWorkTracker.Add(args.WorkId, job);
            }

            MyPrecalcComponent.EnqueueBack(job);
        }
        // Adds a triangle and connects it to the other triangles in the mesh by the given edges.
        // Because connecting by vertices would produce non-manifold meshes, we connect triangles by their edges.
        // When a triangle is added that connects to another triangle only by a vertex, the two touching vertices will be regarded
        // as two separate vertices and they will only be merged when another triangle is added that shares two edges with the two
        // original triangles.
        //
        // When the method returns, edgeAB, edgeBC and edgeCA will contain the indices for the new edges.
        // The positions of vertices A, B and C will be unmodified and might not correspond to the real positions of the resulting
        // triangle's vertices (because the positions of the original edge vertices will sometimes be used due to edge merging).
        //
        // Note: The triangle's vertices and edges must be ordered clockwise:
        //                 B
        //                / \
        //               /   \
        //             AB     BC
        //             /       \
        //            /         \
        //           A -- CA --- C
        protected MyNavigationTriangle AddTriangle(ref Vector3 A, ref Vector3 B, ref Vector3 C, ref int edgeAB, ref int edgeBC, ref int edgeCA)
        {
            MyNavigationTriangle newTri = m_triPool.Allocate();

            // There are several cases that need to be handled and they can be distinguished by the number of
            // existing edges and their shared vertices.

            int newEdgeCount = 0;

            newEdgeCount += edgeAB == -1 ? 1 : 0;
            newEdgeCount += edgeBC == -1 ? 1 : 0;
            newEdgeCount += edgeCA == -1 ? 1 : 0;

            int newTriIndex = -1;

            if (newEdgeCount == 3)
            {
                newTriIndex = m_mesh.MakeNewTriangle(newTri, ref A, ref B, ref C, out edgeAB, out edgeBC, out edgeCA);
            }
            else if (newEdgeCount == 2)
            {
                if (edgeAB != -1)
                {
                    newTriIndex = m_mesh.ExtrudeTriangleFromEdge(ref C, edgeAB, newTri, out edgeBC, out edgeCA);
                }
                else if (edgeBC != -1)
                {
                    newTriIndex = m_mesh.ExtrudeTriangleFromEdge(ref A, edgeBC, newTri, out edgeCA, out edgeAB);
                }
                else
                {
                    newTriIndex = m_mesh.ExtrudeTriangleFromEdge(ref B, edgeCA, newTri, out edgeAB, out edgeBC);
                }
            }
            else if (newEdgeCount == 1)
            {
                if (edgeAB == -1)
                {
                    newTriIndex = GetTriangleOneNewEdge(ref edgeAB, ref edgeBC, ref edgeCA, newTri);
                }
                else if (edgeBC == -1)
                {
                    newTriIndex = GetTriangleOneNewEdge(ref edgeBC, ref edgeCA, ref edgeAB, newTri);
                }
                else
                {
                    newTriIndex = GetTriangleOneNewEdge(ref edgeCA, ref edgeAB, ref edgeBC, newTri);
                }
            }
            else
            {
                var entryAB = m_mesh.GetEdge(edgeAB);
                var entryBC = m_mesh.GetEdge(edgeBC);
                var entryCA = m_mesh.GetEdge(edgeCA);
                int sharedA = entryCA.TryGetSharedVertex(ref entryAB);
                int sharedB = entryAB.TryGetSharedVertex(ref entryBC);
                int sharedC = entryBC.TryGetSharedVertex(ref entryCA);

                int sharedVertCount = 0;
                sharedVertCount += sharedA == -1 ? 0 : 1;
                sharedVertCount += sharedB == -1 ? 0 : 1;
                sharedVertCount += sharedC == -1 ? 0 : 1;

                if (sharedVertCount == 3)
                {
                    newTriIndex = m_mesh.MakeFace(newTri, edgeAB);
                }
                else if (sharedVertCount == 2)
                {
                    if (sharedA == -1)
                    {
                        newTriIndex = GetTriangleTwoSharedVertices(edgeAB, edgeBC, ref edgeCA, sharedB, sharedC, newTri);
                    }
                    else if (sharedB == -1)
                    {
                        newTriIndex = GetTriangleTwoSharedVertices(edgeBC, edgeCA, ref edgeAB, sharedC, sharedA, newTri);
                    }
                    else
                    {
                        newTriIndex = GetTriangleTwoSharedVertices(edgeCA, edgeAB, ref edgeBC, sharedA, sharedB, newTri);
                    }
                }
                else if (sharedVertCount == 1)
                {
                    if (sharedA != -1)
                    {
                        newTriIndex = GetTriangleOneSharedVertex(edgeCA, edgeAB, ref edgeBC, sharedA, newTri);
                    }
                    else if (sharedB != -1)
                    {
                        newTriIndex = GetTriangleOneSharedVertex(edgeAB, edgeBC, ref edgeCA, sharedB, newTri);
                    }
                    else
                    {
                        newTriIndex = GetTriangleOneSharedVertex(edgeBC, edgeCA, ref edgeAB, sharedC, newTri);
                    }
                }
                else
                {
                    int next, prev;
                    newTriIndex = m_mesh.ExtrudeTriangleFromEdge(ref C, edgeAB, newTri, out next, out prev);
                    m_mesh.MergeEdges(prev, edgeCA);
                    m_mesh.MergeEdges(next, edgeBC);
                }
            }

            newTri.Init(this, newTriIndex);
            return(newTri);
        }
 internal static MySingleInstance Allocate()
 {
     return(m_objectPool.Allocate());
 }
Beispiel #11
0
 internal BoundingFrustumD AllocateFrustum()
 {
     return(m_frustumPool.Allocate());
 }
        private bool AddCell(Vector3I cellPos)
        {
            MyCellCoord coord = new MyCellCoord(NAVMESH_LOD, cellPos);

            var geometry = m_voxelMap.Storage.Geometry;

            MyVoxelGeometry.CellData data = geometry.GetCell(ref coord);
            if (data == null)
            {
                m_processedCells.Add(ref cellPos);
                m_higherLevelHelper.AddExplored(ref cellPos);
                return(false);
            }

            ulong packedCoord = coord.PackId64();

            List <DebugDrawEdge> debugEdgesList = new List <DebugDrawEdge>();

            m_debugCellEdges[packedCoord] = debugEdgesList;

            MyVoxelPathfinding.CellId cellId = new MyVoxelPathfinding.CellId()
            {
                VoxelMap = m_voxelMap, Pos = cellPos
            };

            MyTrace.Send(TraceWindow.Ai, "Adding cell " + cellPos);

            m_connectionHelper.ClearCell();
            m_vertexMapping.Init(data.VoxelVerticesCount);

            // Prepare list of possibly intersecting cube grids for voxel-grid navmesh intersection testing
            Vector3D     bbMin  = m_voxelMap.PositionLeftBottomCorner + (m_cellSize * (new Vector3D(-0.125) + cellPos));
            Vector3D     bbMax  = m_voxelMap.PositionLeftBottomCorner + (m_cellSize * (Vector3D.One + cellPos));
            BoundingBoxD cellBB = new BoundingBoxD(bbMin, bbMax);

            m_tmpGridList.Clear();
            m_navmeshCoordinator.PrepareVoxelTriangleTests(cellBB, m_tmpGridList);

            Vector3D voxelMapCenter     = m_voxelMap.PositionComp.GetPosition();
            Vector3  centerDisplacement = voxelMapCenter - m_voxelMap.PositionLeftBottomCorner;

            // This is needed for correct edge classification - to tell, whether the edges are inner or outer edges of the cell
            ProfilerShort.Begin("Triangle preprocessing");
            for (int i = 0; i < data.VoxelTrianglesCount; i++)
            {
                short a = data.VoxelTriangles[i].VertexIndex0;
                short b = data.VoxelTriangles[i].VertexIndex1;
                short c = data.VoxelTriangles[i].VertexIndex2;

                Vector3 aPos, bPos, cPos;
                Vector3 vert;
                data.GetUnpackedPosition(a, out vert);
                aPos = vert - centerDisplacement;
                data.GetUnpackedPosition(b, out vert);
                bPos = vert - centerDisplacement;
                data.GetUnpackedPosition(c, out vert);
                cPos = vert - centerDisplacement;

                bool invalidTriangle = false;
                if ((bPos - aPos).LengthSquared() <= MyVoxelConnectionHelper.OUTER_EDGE_EPSILON_SQ)
                {
                    m_vertexMapping.Union(a, b);
                    invalidTriangle = true;
                }
                if ((cPos - aPos).LengthSquared() <= MyVoxelConnectionHelper.OUTER_EDGE_EPSILON_SQ)
                {
                    m_vertexMapping.Union(a, c);
                    invalidTriangle = true;
                }
                if ((cPos - bPos).LengthSquared() <= MyVoxelConnectionHelper.OUTER_EDGE_EPSILON_SQ)
                {
                    m_vertexMapping.Union(b, c);
                    invalidTriangle = true;
                }

                if (invalidTriangle)
                {
                    continue;
                }

                m_connectionHelper.PreprocessInnerEdge(a, b);
                m_connectionHelper.PreprocessInnerEdge(b, c);
                m_connectionHelper.PreprocessInnerEdge(c, a);
            }
            ProfilerShort.End();

            ProfilerShort.Begin("Free face sorting");
            // Ensure that the faces have increasing index numbers
            Mesh.SortFreeFaces();
            ProfilerShort.End();

            m_higherLevelHelper.OpenNewCell(coord);

            ProfilerShort.Begin("Adding triangles");
            for (int i = 0; i < data.VoxelTrianglesCount; i++)
            {
                short a    = data.VoxelTriangles[i].VertexIndex0;
                short b    = data.VoxelTriangles[i].VertexIndex1;
                short c    = data.VoxelTriangles[i].VertexIndex2;
                short setA = (short)m_vertexMapping.Find(a);
                short setB = (short)m_vertexMapping.Find(b);
                short setC = (short)m_vertexMapping.Find(c);

                if (setA == setB || setB == setC || setA == setC)
                {
                    continue;
                }

                Vector3 aPos, bPos, cPos;
                Vector3 vert;
                data.GetUnpackedPosition(setA, out vert);
                aPos = vert - centerDisplacement;
                data.GetUnpackedPosition(setB, out vert);
                bPos = vert - centerDisplacement;
                data.GetUnpackedPosition(setC, out vert);
                cPos = vert - centerDisplacement;

                if (MyFakes.NAVMESH_PRESUMES_DOWNWARD_GRAVITY)
                {
                    Vector3 normal = (cPos - aPos).Cross(bPos - aPos);
                    normal.Normalize();
                    if (normal.Dot(ref Vector3.Up) <= Math.Cos(MathHelper.ToRadians(54.0f)))
                    {
                        continue;
                    }
                }

                Vector3D aTformed = aPos + voxelMapCenter;
                Vector3D bTformed = bPos + voxelMapCenter;
                Vector3D cTformed = cPos + voxelMapCenter;

                bool intersecting = false;
                m_tmpLinkCandidates.Clear();
                m_navmeshCoordinator.TestVoxelNavmeshTriangle(ref aTformed, ref bTformed, ref cTformed, m_tmpGridList, m_tmpLinkCandidates, out intersecting);
                if (intersecting)
                {
                    m_tmpLinkCandidates.Clear();
                    continue;
                }

                if (!m_connectionHelper.IsInnerEdge(a, b))
                {
                    debugEdgesList.Add(new DebugDrawEdge(aTformed, bTformed));
                }
                if (!m_connectionHelper.IsInnerEdge(b, c))
                {
                    debugEdgesList.Add(new DebugDrawEdge(bTformed, cTformed));
                }
                if (!m_connectionHelper.IsInnerEdge(c, a))
                {
                    debugEdgesList.Add(new DebugDrawEdge(cTformed, aTformed));
                }

                int edgeAB   = m_connectionHelper.TryGetAndRemoveEdgeIndex(b, a, ref bPos, ref aPos);
                int edgeBC   = m_connectionHelper.TryGetAndRemoveEdgeIndex(c, b, ref cPos, ref bPos);
                int edgeCA   = m_connectionHelper.TryGetAndRemoveEdgeIndex(a, c, ref aPos, ref cPos);
                int formerAB = edgeAB;
                int formerBC = edgeBC;
                int formerCA = edgeCA;

                ProfilerShort.Begin("AddTriangle");
                var tri = AddTriangle(ref aPos, ref bPos, ref cPos, ref edgeAB, ref edgeBC, ref edgeCA);
                ProfilerShort.End();

                CheckMeshConsistency();

                m_higherLevelHelper.AddTriangle(tri.Index);

                if (formerAB == -1)
                {
                    m_connectionHelper.AddEdgeIndex(a, b, ref aPos, ref bPos, edgeAB);
                }
                if (formerBC == -1)
                {
                    m_connectionHelper.AddEdgeIndex(b, c, ref bPos, ref cPos, edgeBC);
                }
                if (formerCA == -1)
                {
                    m_connectionHelper.AddEdgeIndex(c, a, ref cPos, ref aPos, edgeCA);
                }

                // TODO: Instead of this, just add the tri into a list of tris that want to connect with the link candidates
                //m_navmeshCoordinator.TryAddVoxelNavmeshLinks(tri, cellId, m_tmpLinkCandidates);
                foreach (var candidate in m_tmpLinkCandidates)
                {
                    List <MyNavigationPrimitive> primitives = null;
                    if (!m_tmpCubeLinkCandidates.TryGetValue(candidate, out primitives))
                    {
                        primitives = m_primitiveListPool.Allocate();
                        m_tmpCubeLinkCandidates.Add(candidate, primitives);
                    }

                    primitives.Add(tri);
                }
                m_tmpLinkCandidates.Clear();
            }
            ProfilerShort.End();

            m_tmpGridList.Clear();
            m_connectionHelper.ClearCell();
            m_vertexMapping.Clear();

            Debug.Assert(!m_processedCells.Contains(ref cellPos));
            m_processedCells.Add(ref cellPos);
            m_higherLevelHelper.AddExplored(ref cellPos);

            // Find connected components in the current cell's subgraph of the navigation mesh
            m_higherLevelHelper.ProcessCellComponents();
            m_higherLevelHelper.CloseCell();

            // Create navmesh links using the navmesh coordinator, taking into consideration the high level components
            m_navmeshCoordinator.TryAddVoxelNavmeshLinks2(cellId, m_tmpCubeLinkCandidates);
            m_navmeshCoordinator.UpdateVoxelNavmeshCellHighLevelLinks(cellId);

            foreach (var candidate in m_tmpCubeLinkCandidates)
            {
                candidate.Value.Clear();
                m_primitiveListPool.Deallocate(candidate.Value);
            }
            m_tmpCubeLinkCandidates.Clear();

            return(true);
        }
Beispiel #13
0
        internal void RecordCommandLists(MyCullQuery processedCullQuery, Queue <CommandList> outCommandLists)
        {
            ProfilerShort.Begin("PrepareWork");

            ProfilerShort.Begin("Init");
            Debug.Assert(m_workList.Count == 0, "Work list not cleared after use!");

            foreach (List <MyRenderCullResultFlat> cullResults in m_passElements)
            {
                cullResults.Clear();
                m_sortListPool.Deallocate(cullResults);
            }
            m_passElements.Clear();
            m_passElements2.Clear();

            for (int renderPassIndex = 0; renderPassIndex < processedCullQuery.Size; ++renderPassIndex)
            {
                if (!MyRender11.DeferredContextsEnabled)
                {
                    processedCullQuery.RenderingPasses[renderPassIndex].SetImmediate(true);
                }

                m_passElements.Add(m_sortListPool.Allocate());
                m_passElements2.Add(null);
            }

            ProfilerShort.BeginNextBlock("Flatten");
            for (int i = 0; i < processedCullQuery.Size; ++i)
            {
                m_affectedQueueIds.SetSize(0);
                var frustumQuery = processedCullQuery.FrustumCullQueries[i];

                for (int renderPassIndex = 0; renderPassIndex < processedCullQuery.Size; renderPassIndex++)
                {
                    if ((processedCullQuery.RenderingPasses[renderPassIndex].ProcessingMask & frustumQuery.Bitmask) > 0)
                    {
                        m_affectedQueueIds.Add(renderPassIndex);
                    }
                }

                var cullProxies = frustumQuery.List;
                var queryType   = frustumQuery.Type;

                foreach (MyCullProxy cullProxy in cullProxies)
                {
                    var renderableProxies = cullProxy.RenderableProxies;
                    if (renderableProxies == null)
                    {
                        continue;
                    }

                    for (int proxyIndex = 0; proxyIndex < renderableProxies.Length; ++proxyIndex)
                    {
                        var flag = renderableProxies[proxyIndex].DrawSubmesh.Flags;
                        if (queryType == MyFrustumEnum.MainFrustum)
                        {
                            if ((flag & MyDrawSubmesh.MySubmeshFlags.Gbuffer) != MyDrawSubmesh.MySubmeshFlags.Gbuffer)
                            {
                                continue;
                            }
                        }
                        else if (queryType == MyFrustumEnum.ShadowCascade || queryType == MyFrustumEnum.ShadowProjection)
                        {
                            if ((flag & MyDrawSubmesh.MySubmeshFlags.Depth) != MyDrawSubmesh.MySubmeshFlags.Depth)
                            {
                                continue;
                            }
                        }

                        MyRenderableProxy renderableProxy = renderableProxies[proxyIndex];
                        ulong             sortKey         = cullProxy.SortingKeys[proxyIndex];

                        for (int queueIndex = 0; queueIndex < m_affectedQueueIds.Count; ++queueIndex)
                        {
                            var queueId = m_affectedQueueIds[queueIndex];
                            var item    = new MyRenderCullResultFlat
                            {
                                SortKey     = sortKey,
                                RenderProxy = renderableProxy,
                            };

                            m_passElements[queueId].Add(item);
                        }
                    }
                }

                // proxy 2
                var list2 = frustumQuery.List2;

                // flatten and sort
                m_flattenedKeys.SetSize(0);
                m_indirectionList.SetSize(0);
                m_location.SetSize(0);

                int indirectionCounter = 0;
                for (int list2Index = 0; list2Index < list2.Count; ++list2Index)
                {
                    for (int sortKeyIndex = 0; sortKeyIndex < list2[list2Index].SortingKeys.Length; sortKeyIndex++)
                    {
                        m_flattenedKeys.Add(list2[list2Index].SortingKeys[sortKeyIndex]);
                        m_indirectionList.Add(indirectionCounter++);
                        m_location.Add(MyTuple.Create(list2Index, sortKeyIndex));
                    }
                }

                MyRenderableProxy_2[] flattenedProxies = null;

                if (indirectionCounter > 0)
                {
                    flattenedProxies = new MyRenderableProxy_2[indirectionCounter];
                }

                m_sortingKeysComparer.Values = m_flattenedKeys;
                m_indirectionList.Sort(0, m_indirectionList.Count, m_sortingKeysComparer);

                if (flattenedProxies != null)
                {
                    for (int e = 0; e < indirectionCounter; e++)
                    {
                        var l = m_location[m_indirectionList[e]];
                        flattenedProxies[e] = list2[l.Item1].Proxies[l.Item2];
                    }
                }

                for (int l = 0; l < m_affectedQueueIds.Count; l++)
                {
                    m_passElements2[m_affectedQueueIds[l]] = flattenedProxies;
                }
            }
            ProfilerShort.BeginNextBlock("Sort");
            foreach (var flatCullResults in m_passElements)
            {
                foreach (MyRenderCullResultFlat element in flatCullResults)
                {
                    List <MyRenderCullResultFlat> sortList;
                    if (m_tmpSortListDictionary.TryGetValue(element.SortKey, out sortList))
                    {
                        sortList.Add(element);
                    }
                    else
                    {
                        sortList = m_sortListPool.Allocate();
                        sortList.Add(element);
                        m_tmpSortListDictionary.Add(element.SortKey, sortList);
                    }
                }
                flatCullResults.Clear();
                foreach (var sortList in m_tmpSortListDictionary.Values)
                {
                    flatCullResults.AddList(sortList);
                    sortList.SetSize(0);
                    m_sortListPool.Deallocate(sortList);
                }
                m_tmpSortListDictionary.Clear();
            }

            int jobsNum = GetRenderingThreadsNum();

            // always amortize this path
            ProfilerShort.BeginNextBlock("WorkAmortization");

            //passElements.RemoveAll(x => x.Count == 0);

            int workSum = 0;

            foreach (var list in m_passElements)
            {
                workSum += list.Count;
            }

            int batchWork = (workSum + jobsNum - 1) / jobsNum;

            Debug.Assert(m_subworks.Count == 0);

            int work = 0;

            for (int passElementIndex = 0; passElementIndex < m_passElements.Count; ++passElementIndex)
            {
                var flatCullResults = m_passElements[passElementIndex];
                if (flatCullResults.Count == 0)
                {
                    MyObjectPoolManager.Deallocate(processedCullQuery.RenderingPasses[passElementIndex]);
                    processedCullQuery.RenderingPasses[passElementIndex] = null;
                    if (m_passElements2[passElementIndex] == null || m_passElements2[passElementIndex].Length == 0)
                    {
                        continue;
                    }
                }

                if (processedCullQuery.RenderingPasses[passElementIndex] == null)
                {
                    continue;
                }

                int passBegin = 0;

                if (m_passElements2[passElementIndex] != null && m_passElements2[passElementIndex].Length > 0)
                {
                    m_subworks.Add(new MyRenderingWorkItem
                    {
                        Pass  = processedCullQuery.RenderingPasses[passElementIndex].Fork(),
                        List2 = m_passElements2[passElementIndex]
                    });
                }

                while (passBegin < flatCullResults.Count)
                {
                    int toTake = Math.Min(flatCullResults.Count - passBegin, batchWork - work);

                    var workItem = new MyRenderingWorkItem
                    {
                        Renderables = flatCullResults,
                        Begin       = passBegin,
                        End         = passBegin + toTake
                    };

                    if (toTake < flatCullResults.Count && workItem.End != workItem.Renderables.Count)
                    {
                        workItem.Pass = processedCullQuery.RenderingPasses[passElementIndex].Fork();
                    }
                    else
                    {
                        workItem.Pass = processedCullQuery.RenderingPasses[passElementIndex];
                        processedCullQuery.RenderingPasses[passElementIndex] = null;    // Consume the pass so it doesn't get cleaned up later with the cull query, but instead with the work item
                    }

                    m_subworks.Add(workItem);

                    passBegin += toTake;
                    work      += toTake;

                    Debug.Assert(work <= batchWork);
                    if (work != batchWork)
                    {
                        continue;
                    }

                    if (MyRender11.DeferredContextsEnabled)
                    {
                        var renderWork = MyObjectPoolManager.Allocate <MyRenderingWorkRecordCommands>();
                        renderWork.Init(MyRenderContextPool.AcquireRC(), m_subworks);
                        m_workList.Add(renderWork);
                    }
                    else
                    {
                        var renderWork = MyObjectPoolManager.Allocate <MyRenderingWorkRecordCommands>();
                        renderWork.Init(m_subworks);
                        m_workList.Add(renderWork);
                    }

                    work = 0;

                    m_subworks.Clear();
                }
            }
            if (m_subworks.Count > 0)
            {
                if (MyRender11.DeferredContextsEnabled)
                {
                    var renderWork = MyObjectPoolManager.Allocate <MyRenderingWorkRecordCommands>();
                    renderWork.Init(MyRenderContextPool.AcquireRC(), m_subworks);
                    m_workList.Add(renderWork);
                }
                else
                {
                    var renderWork = MyObjectPoolManager.Allocate <MyRenderingWorkRecordCommands>();
                    renderWork.Init(m_subworks);
                    m_workList.Add(renderWork);
                }
                m_subworks.Clear();
            }

            ProfilerShort.End();

            ProfilerShort.End();

            DoRecordingWork(outCommandLists);

            foreach (var renderWork in m_workList)
            {
                MyObjectPoolManager.Deallocate(renderWork);
            }
            m_workList.Clear();
        }