/// <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); } } } }
/// <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(); }
public RenderViewRestore(RenderContext renderContext) { context = renderContext; previousValue = renderContext.RenderView; }
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); } }