Example #1
0
        /// <inheritdoc/>
        public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
        {
            var commandList = context.CommandList;

            foreach (var renderFeature in RenderFeatures)
            {
                renderFeature.Draw(context, renderView, renderViewStage, startIndex, endIndex);
            }

            // TODO: stackalloc?
            var descriptorSetsLocal = descriptorSets.Value;

            if (descriptorSetsLocal is null || descriptorSetsLocal.Length < EffectDescriptorSetSlotCount)
            {
                descriptorSetsLocal = descriptorSets.Value = new DescriptorSet[EffectDescriptorSetSlotCount];
            }

            MeshDraw currentDrawData = null;
            int      emptyBufferSlot = -1;

            for (int index = startIndex; index < endIndex; index++)
            {
                var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode;
                var renderNode          = GetRenderNode(renderNodeReference);

                var renderMesh = (RenderMesh)renderNode.RenderObject;
                var drawData   = renderMesh.ActiveMeshDraw;

                // Get effect
                // TODO: Use real effect slot
                var renderEffect = renderNode.RenderEffect;
                if (renderEffect.Effect == null)
                {
                    continue;
                }

                // Bind VB
                if (currentDrawData != drawData)
                {
                    for (int slot = 0; slot < drawData.VertexBuffers.Length; slot++)
                    {
                        var vertexBuffer = drawData.VertexBuffers[slot];
                        commandList.SetVertexBuffer(slot, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride);
                    }

                    // If the mesh's vertex buffers miss any input streams, an additional input binding will have been added to the pipeline state.
                    // We bind an additional empty vertex buffer to that slot handle those streams gracefully.
                    if (emptyBufferSlot != drawData.VertexBuffers.Length)
                    {
                        commandList.SetVertexBuffer(drawData.VertexBuffers.Length, emptyBuffer, 0, 0);
                        emptyBufferSlot = drawData.VertexBuffers.Length;
                    }

                    if (drawData.IndexBuffer != null)
                    {
                        commandList.SetIndexBuffer(drawData.IndexBuffer.Buffer, drawData.IndexBuffer.Offset, drawData.IndexBuffer.Is32Bit);
                    }
                    currentDrawData = drawData;
                }

                var resourceGroupOffset = ComputeResourceGroupOffset(renderNodeReference);

                // Update cbuffer
                renderEffect.Reflection.BufferUploader.Apply(context.CommandList, ResourceGroupPool, resourceGroupOffset);

                // Bind descriptor sets
                for (int i = 0; i < descriptorSetsLocal.Length; ++i)
                {
                    var resourceGroup = ResourceGroupPool[resourceGroupOffset++];
                    if (resourceGroup != null)
                    {
                        descriptorSetsLocal[i] = resourceGroup.DescriptorSet;
                    }
                }

                commandList.SetPipelineState(renderEffect.PipelineState);
                commandList.SetDescriptorSets(0, descriptorSetsLocal);

                // Draw
                if (drawData.IndexBuffer is null)
                {
                    if (renderMesh.InstanceCount > 0)
                    {
                        commandList.DrawInstanced(drawData.DrawCount, renderMesh.InstanceCount, drawData.StartLocation);
                    }
                    else
                    {
                        commandList.Draw(drawData.DrawCount, drawData.StartLocation);
                    }
                }
                else
                {
                    if (renderMesh.InstanceCount > 0)
                    {
                        commandList.DrawIndexedInstanced(drawData.DrawCount, renderMesh.InstanceCount, drawData.StartLocation);
                    }
                    else
                    {
                        commandList.DrawIndexed(drawData.DrawCount, drawData.StartLocation);
                    }
                }
            }
        }
Example #2
0
        /// <summary>
        /// Collects render objects visibile in a view (if not previously collected before).
        /// </summary>
        /// <param name="view"></param>
        public void TryCollect(RenderView view)
        {
            // Already colleted this frame?
            if (view.LastFrameCollected == RenderSystem.FrameCounter)
            {
                return;
            }

            view.LastFrameCollected = RenderSystem.FrameCounter;

            ReevaluateActiveRenderStages();

            // Collect objects, and perform frustum culling
            // TODO GRAPHICS REFACTOR Create "VisibilityObject" (could contain multiple RenderNode) and separate frustum culling from RenderSystem
            // TODO GRAPHICS REFACTOR optimization: maybe we could process all views at once (swap loop between per object and per view)

            // View bounds calculation
            view.MinimumDistance = float.PositiveInfinity;
            view.MaximumDistance = float.NegativeInfinity;

            Matrix.Invert(ref view.View, out var viewInverse);
            var planeNormal  = viewInverse.Forward;
            var pointOnPlane = viewInverse.TranslationVector + viewInverse.Forward * view.NearClipPlane;
            var plane        = new Plane(planeNormal, Vector3.Dot(pointOnPlane, planeNormal)); // TODO: Point-normal-constructor seems wrong. Check.

            // Prepare culling mask
            foreach (var renderViewStage in view.RenderStages)
            {
                var renderStageIndex = renderViewStage.Index;
                viewRenderStageMask[renderStageIndex / RenderStageMaskSizePerEntry] |= 1U << (renderStageIndex % RenderStageMaskSizePerEntry);
            }

            // Create the bounding frustum locally on the stack, so that frustum.Contains is performed with boundingBox that is also on the stack
            // TODO GRAPHICS REFACTOR frustum culling is currently hardcoded (cf previous TODO, we should make this more modular and move it out of here)
            var frustum     = new BoundingFrustum(ref view.ViewProjection);
            var cullingMode = DisableCulling ? CameraCullingMode.None : view.CullingMode;

            // TODO GRAPHICS REFACTOR we currently forward SceneCameraRenderer.CullingMask
            // Note sure this is really a good mechanism long term (it forces to recreate multiple time the same view, instead of using RenderStage + selectors or a similar mechanism)
            // This is still supported so that existing gizmo code kept working with new graphics refactor. Might be reconsidered at some point.
            var cullingMask = view.CullingMask;

            // Process objects
            //foreach (var renderObject in RenderObjects)
            //Dispatcher.ForEach(RenderObjects, renderObject =>
            Dispatcher.For(0, RenderObjects.Count, () => collectorCache.Value, (index, cache) =>
            {
                var renderObject = RenderObjects[index];

                // Skip not enabled objects
                if (!renderObject.Enabled || ((RenderGroupMask)(1U << (int)renderObject.RenderGroup) & cullingMask) == 0)
                {
                    return;
                }

                var renderStageMask     = RenderData.GetData(RenderStageMaskKey);
                var renderStageMaskNode = renderObject.VisibilityObjectNode * stageMaskMultiplier;

                // Determine if this render object belongs to this view
                bool renderStageMatch = false;
                unsafe
                {
                    fixed(uint *viewRenderStageMaskStart   = viewRenderStageMask)
                    fixed(uint *objectRenderStageMaskStart = renderStageMask.Data)
                    {
                        var viewRenderStageMaskPtr   = viewRenderStageMaskStart;
                        var objectRenderStageMaskPtr = objectRenderStageMaskStart + renderStageMaskNode.Index;
                        for (int i = 0; i < viewRenderStageMask.Length; ++i)
                        {
                            if ((*viewRenderStageMaskPtr++ & *objectRenderStageMaskPtr++) != 0)
                            {
                                renderStageMatch = true;
                                break;
                            }
                        }
                    }
                }

                // Object not part of this view because no render stages in this objects are visible in this view
                if (!renderStageMatch)
                {
                    return;
                }

                // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/
                // Compute transformed AABB (by world)
                if (cullingMode == CameraCullingMode.Frustum &&
                    renderObject.BoundingBox.Extent != Vector3.Zero &&
                    !FrustumContainsBox(ref frustum, ref renderObject.BoundingBox, view.VisiblityIgnoreDepthPlanes))
                {
                    return;
                }

                // Add object to list of visible objects
                // TODO GRAPHICS REFACTOR we should be able to push multiple elements with future VisibilityObject
                view.RenderObjects.Add(renderObject, cache);

                // Calculate bounding box of all render objects in the view
                if (renderObject.BoundingBox.Extent != Vector3.Zero)
                {
                    CalculateMinMaxDistance(ref plane, ref renderObject.BoundingBox, ref view.MinimumDistance, ref view.MaximumDistance);
                }
            }, cache => cache.Flush());

            view.RenderObjects.Close();
        }
Example #3
0
 public RenderViewRestore(RenderContext renderContext)
 {
     context       = renderContext;
     previousValue = renderContext.RenderView;
 }
Example #4
0
        public void Draw(RenderDrawContext renderDrawContext, RenderView renderView, RenderStage renderStage)
        {
            // Sync point: draw (from now, we should execute with a graphics device context to perform rendering)

            // Look for the RenderViewStage corresponding to this RenderView | RenderStage combination
            var renderViewStage = RenderViewStage.Invalid;

            foreach (var currentRenderViewStage in renderView.RenderStages)
            {
                if (currentRenderViewStage.Index == renderStage.Index)
                {
                    renderViewStage = currentRenderViewStage;
                    break;
                }
            }

            if (renderViewStage.Index == -1)
            {
                throw new InvalidOperationException("Requested RenderView|RenderStage combination doesn't exist. Please add it to RenderView.RenderStages.");
            }

            // Perform updates once per change of RenderView
            foreach (var renderFeature in RenderFeatures)
            {
                renderFeature.Draw(renderDrawContext, renderView, renderViewStage);
            }

            // Generate and execute draw jobs
            var renderNodes     = renderViewStage.SortedRenderNodes;
            var renderNodeCount = renderViewStage.RenderNodes.Count;

            if (renderNodeCount == 0)
            {
                return;
            }

            if (!GraphicsDevice.IsDeferred)
            {
                int currentStart, currentEnd;
                for (currentStart = 0; currentStart < renderNodeCount; currentStart = currentEnd)
                {
                    var currentRenderFeature = renderNodes[currentStart].RootRenderFeature;
                    currentEnd = currentStart + 1;
                    while (currentEnd < renderNodeCount && renderNodes[currentEnd].RootRenderFeature == currentRenderFeature)
                    {
                        currentEnd++;
                    }

                    // Divide into task chunks for parallelism
                    currentRenderFeature.Draw(renderDrawContext, renderView, renderViewStage, currentStart, currentEnd);
                }
            }
            else
            {
                // Create at most one batch per processor
                int batchCount = Math.Min(Environment.ProcessorCount, renderNodeCount);
                int batchSize  = (renderNodeCount + (batchCount - 1)) / batchCount;
                batchCount = (renderNodeCount + (batchSize - 1)) / batchSize;

                // Remember state
                var depthStencilBuffer = renderDrawContext.CommandList.DepthStencilBuffer;
                int renderTargetCount  = renderDrawContext.CommandList.RenderTargetCount;
                if (renderTargets == null)
                {
                    renderTargets = new Texture[renderDrawContext.CommandList.RenderTargets.Length];
                }
                for (int i = 0; i < renderTargetCount; ++i)
                {
                    renderTargets[i] = renderDrawContext.CommandList.RenderTargets[i];
                }
                var viewport = renderDrawContext.CommandList.Viewport;
                var scissor  = renderDrawContext.CommandList.Scissor;

                // Collect one command list per batch and the main one up to this point
                if (commandLists == null || commandLists.Length < batchCount + 1)
                {
                    Array.Resize(ref commandLists, batchCount + 1);
                }
                commandLists[0] = renderDrawContext.CommandList.Close();

                Dispatcher.For(0, batchCount, () => renderDrawContext.RenderContext.GetThreadContext(), (batchIndex, threadContext) =>
                {
                    threadContext.CommandList.Reset();
                    threadContext.CommandList.ClearState();

                    // Transfer state to all command lists
                    threadContext.CommandList.SetRenderTargets(depthStencilBuffer, renderTargetCount, renderTargets);
                    threadContext.CommandList.SetViewport(viewport);
                    threadContext.CommandList.SetScissorRectangle(scissor);

                    var currentStart = batchSize * batchIndex;
                    int currentEnd;

                    var endExclusive = Math.Min(renderNodeCount, currentStart + batchSize);

                    if (endExclusive <= currentStart)
                    {
                        return;
                    }

                    for (; currentStart < endExclusive; currentStart = currentEnd)
                    {
                        var currentRenderFeature = renderNodes[currentStart].RootRenderFeature;
                        currentEnd = currentStart + 1;
                        while (currentEnd < endExclusive && renderNodes[currentEnd].RootRenderFeature == currentRenderFeature)
                        {
                            currentEnd++;
                        }

                        // Divide into task chunks for parallelism
                        currentRenderFeature.Draw(threadContext, renderView, renderViewStage, currentStart, currentEnd);
                    }

                    commandLists[batchIndex + 1] = threadContext.CommandList.Close();
                });

                GraphicsDevice.ExecuteCommandLists(batchCount + 1, commandLists);

                renderDrawContext.CommandList.Reset();
                renderDrawContext.CommandList.ClearState();

                // Reapply previous state
                renderDrawContext.CommandList.SetRenderTargets(depthStencilBuffer, renderTargetCount, renderTargets);
                renderDrawContext.CommandList.SetViewport(viewport);
                renderDrawContext.CommandList.SetScissorRectangle(scissor);
            }
        }