public override unsafe void Prepare(RenderDrawContext context) { var previousTransformationInfoData = rootRenderFeature.RenderData.GetData(previousTransformationInfoKey); var previousTransformationViewInfoData = rootRenderFeature.RenderData.GetData(previousTransformationViewInfoKey); var renderModelObjectInfoData = rootRenderFeature.RenderData.GetData(renderModelObjectInfoKey); // Calculate previous WVP matrix per view and object usageCounter++; for (int index = 0; index < RenderSystem.Views.Count; index++) { var view = RenderSystem.Views[index]; var viewFeature = view.Features[rootRenderFeature.Index]; bool useView = false; foreach (var stage in RenderSystem.RenderStages) { foreach (var renderViewStage in view.RenderStages) { if (renderViewStage.Index == stage.Index && stage.OutputValidator?.Find <VelocityTargetSemantic>() >= 0) { useView = true; break; } } } if (!useView) { continue; } // Cache per-view data locally RenderViewData viewData; if (!renderViewDatas.TryGetValue(view, out viewData)) { viewData = new RenderViewData(); renderViewDatas.Add(view, viewData); } Dispatcher.ForEach(viewFeature.ViewObjectNodes, renderPerViewNodeReference => { var renderPerViewNode = rootRenderFeature.GetViewObjectNode(renderPerViewNodeReference); var renderModelFrameInfo = renderModelObjectInfoData[renderPerViewNode.ObjectNode]; Matrix previousViewProjection = viewData.PreviousViewProjection; Matrix previousWorldViewProjection; Matrix.Multiply(ref renderModelFrameInfo.World, ref previousViewProjection, out previousWorldViewProjection); previousTransformationViewInfoData[renderPerViewNodeReference] = new PreviousObjectViewInfo { WorldViewProjection = previousWorldViewProjection, }; }); // Shift current view projection transform into previous viewData.PreviousViewProjection = view.ViewProjection; viewData.UsageCounter = usageCounter; updatedViews.Add(view); } foreach (var view in renderViewDatas.Keys.ToArray()) { if (!updatedViews.Contains(view)) { renderViewDatas.Remove(view); } } updatedViews.Clear(); // Update cbuffer for previous WVP matrix Dispatcher.ForEach(((RootEffectRenderFeature)rootRenderFeature).RenderNodes, (ref RenderNode renderNode) => { var perDrawLayout = renderNode.RenderEffect.Reflection.PerDrawLayout; if (perDrawLayout == null) { return; } var previousWvpOffset = perDrawLayout.GetConstantBufferOffset(previousWorldViewProjection); if (previousWvpOffset == -1) { return; } var mappedCB = renderNode.Resources.ConstantBuffer.Data; var previousPerDraw = (PreviousPerDraw *)((byte *)mappedCB + previousWvpOffset); var renderModelFrameInfo = renderModelObjectInfoData[renderNode.RenderObject.ObjectNode]; var renderModelPreviousFrameInfo = previousTransformationViewInfoData[renderNode.ViewObjectNode]; // Shift current world transform into previous transform previousTransformationInfoData[renderNode.RenderObject.StaticObjectNode] = new StaticObjectInfo { World = renderModelFrameInfo.World, }; previousPerDraw->PreviousWorldViewProjection = renderModelPreviousFrameInfo.WorldViewProjection; }); }
/// <summary> /// Performs most of the work (computation and resource preparation). Later game simulation might be running during that step. /// </summary> /// <param name="context"></param> public unsafe void Prepare(RenderDrawContext context) { // Sync point: after extract, before prepare (game simulation could resume now) // Generate and execute prepare effect jobs foreach (var renderFeature in RenderFeatures) // We might be able to parallelize too as long as we resepect render feature dependency graph (probably very few dependencies in practice) { // Divide into task chunks for parallelism renderFeature.PrepareEffectPermutations(context); } // Generate and execute prepare jobs foreach (var renderFeature in RenderFeatures) // We might be able to parallelize too as long as we resepect render feature dependency graph (probably very few dependencies in practice) { // Divide into task chunks for parallelism renderFeature.Prepare(context); } // Sort Dispatcher.ForEach(Views, view => { Dispatcher.For(0, view.RenderStages.Count, () => prepareThreadLocals.Acquire(), (index, local) => { var renderViewStage = view.RenderStages[index]; var renderNodes = renderViewStage.RenderNodes; if (renderNodes.Count == 0) { return; } var renderStage = RenderStages[renderViewStage.Index]; var sortedRenderNodes = renderViewStage.SortedRenderNodes; // Fast clear, since it's cleared properly in Reset() sortedRenderNodes.Resize(renderViewStage.RenderNodes.Count, true); if (renderStage.SortMode != null) { // Make sure sortKeys is big enough if (local.SortKeys == null || local.SortKeys.Length < renderNodes.Count) { Array.Resize(ref local.SortKeys, renderNodes.Count); } // renderNodes[start..end] belongs to the same render feature fixed(SortKey * sortKeysPtr = local.SortKeys) renderStage.SortMode.GenerateSortKey(view, renderViewStage, sortKeysPtr); Dispatcher.Sort(local.SortKeys, 0, renderNodes.Count, Comparer <SortKey> .Default); // Reorder list for (int i = 0; i < renderNodes.Count; ++i) { sortedRenderNodes[i] = renderNodes[local.SortKeys[i].Index]; } } else { // No sorting, copy as is for (int i = 0; i < renderNodes.Count; ++i) { sortedRenderNodes[i] = renderNodes[i]; } } }, state => prepareThreadLocals.Release(state)); }); // Flush the resources uploaded during Prepare context.ResourceGroupAllocator.Flush(); context.RenderContext.Flush(); }
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); } }
/// <summary> /// Draws a full screen quad using iterating on each pass of this effect. /// </summary> public void Draw(RenderDrawContext context, string nameFormat, params object[] args) { // TODO: this is alocating a string, we should try to not allocate here. Draw(context, name: string.Format(nameFormat, args)); }