/// <inheritdoc/>
        public override void Prepare(RenderDrawContext context)
        {
            EffectObjectNodes.Clear(false);

            // Make sure descriptor set pool is large enough
            var expectedDescriptorSetPoolSize = RenderNodes.Count * effectDescriptorSetSlots.Count;
            if (ResourceGroupPool.Length < expectedDescriptorSetPoolSize)
                Array.Resize(ref ResourceGroupPool, expectedDescriptorSetPoolSize);

            // Allocate PerFrame, PerView and PerDraw resource groups and constant buffers
            var renderEffects = RenderData.GetData(RenderEffectKey);
            int effectSlotCount = EffectPermutationSlotCount;
            foreach (var view in RenderSystem.Views)
            {
                var viewFeature = view.Features[Index];

                Dispatcher.ForEach(viewFeature.RenderNodes, () => prepareThreadContext.Value, (renderNodeReference, batch) =>
                {
                    var threadContext = batch.Context;
                    var renderNode = this.GetRenderNode(renderNodeReference);
                    var renderObject = renderNode.RenderObject;

                    // Get RenderEffect
                    var staticObjectNode = renderObject.StaticObjectNode;
                    var staticEffectObjectNode = staticObjectNode * effectSlotCount + effectSlots[renderNode.RenderStage.Index].Index;
                    var renderEffect = renderEffects[staticEffectObjectNode];

                    // Not compiled yet?
                    if (renderEffect.Effect == null)
                    {
                        renderNode.RenderEffect = renderEffect;
                        renderNode.EffectObjectNode = EffectObjectNodeReference.Invalid;
                        renderNode.Resources = null;
                        RenderNodes[renderNodeReference.Index] = renderNode;
                        return;
                    }

                    var renderEffectReflection = renderEffect.Reflection;

                    // PerView resources/cbuffer
                    var viewLayout = renderEffectReflection.PerViewLayout;
                    if (viewLayout != null)
                    {
                        var viewCount = RenderSystem.Views.Count;
                        if (viewLayout.Entries?.Length < viewCount)
                        {
                            // TODO: Should this be a first loop?
                            lock (viewLayout)
                            {
                                if (viewLayout.Entries?.Length < viewCount)
                                {
                                    var newEntries = new ResourceGroupEntry[viewCount];

                                    for (int index = 0; index < viewLayout.Entries.Length; index++)
                                        newEntries[index] = viewLayout.Entries[index];

                                    for (int index = viewLayout.Entries.Length; index < viewCount; index++)
                                        newEntries[index].Resources = new ResourceGroup();

                                    viewLayout.Entries = newEntries;
                                }
                            }
                        }

                        if (viewLayout.Entries[view.Index].MarkAsUsed(RenderSystem))
                        {
                            threadContext.ResourceGroupAllocator.PrepareResourceGroup(viewLayout, BufferPoolAllocationType.UsedMultipleTime, viewLayout.Entries[view.Index].Resources);

                            // Register it in list of view layouts to update for this frame
                            viewFeature.Layouts.Add(viewLayout);
                        }
                    }

                    // PerFrame resources/cbuffer
                    var frameLayout = renderEffect.Reflection.PerFrameLayout;
                    if (frameLayout != null && frameLayout.Entry.MarkAsUsed(RenderSystem))
                    {
                        threadContext.ResourceGroupAllocator.PrepareResourceGroup(frameLayout, BufferPoolAllocationType.UsedMultipleTime, frameLayout.Entry.Resources);

                        // Register it in list of view layouts to update for this frame
                        FrameLayouts.Add(frameLayout);
                    }

                    // PerDraw resources/cbuffer
                    // Get nodes
                    var viewObjectNode = GetViewObjectNode(renderNode.ViewObjectNode);

                    // Allocate descriptor set
                    renderNode.Resources = threadContext.ResourceGroupAllocator.AllocateResourceGroup();
                    if (renderEffectReflection.PerDrawLayout != null)
                    {
                        threadContext.ResourceGroupAllocator.PrepareResourceGroup(renderEffectReflection.PerDrawLayout, BufferPoolAllocationType.UsedOnce, renderNode.Resources);
                    }

                    // Create EffectObjectNode
                    var effectObjectNodeIndex = EffectObjectNodes.Add(new EffectObjectNode(renderEffect, viewObjectNode.ObjectNode));

                    // Link to EffectObjectNode (created right after)
                    // TODO: rewrite this
                    renderNode.EffectObjectNode = new EffectObjectNodeReference(effectObjectNodeIndex);

                    renderNode.RenderEffect = renderEffect;
                    
                    // Bind well-known descriptor sets
                    var descriptorSetPoolOffset = ComputeResourceGroupOffset(renderNodeReference);
                    ResourceGroupPool[descriptorSetPoolOffset + perFrameDescriptorSetSlot.Index] = frameLayout?.Entry.Resources;
                    ResourceGroupPool[descriptorSetPoolOffset + perViewDescriptorSetSlot.Index] = renderEffect.Reflection.PerViewLayout?.Entries[view.Index].Resources;
                    ResourceGroupPool[descriptorSetPoolOffset + perDrawDescriptorSetSlot.Index] = renderNode.Resources;

                    // Create resource group for everything else in case of fallback effects
                    if (renderEffect.State != RenderEffectState.Normal && renderEffect.FallbackParameters != null)
                    {
                        if (renderEffect.FallbackParameterUpdater.ResourceGroups == null)
                        {
                            // First time
                            renderEffect.FallbackParameterUpdater = new EffectParameterUpdater(renderEffect.Reflection.FallbackUpdaterLayout, renderEffect.FallbackParameters);
                        }

                        renderEffect.FallbackParameterUpdater.Update(RenderSystem.GraphicsDevice, threadContext.ResourceGroupAllocator, renderEffect.FallbackParameters);

                        var fallbackResourceGroupMapping = renderEffect.Reflection.FallbackResourceGroupMapping;
                        for (int i = 0; i < fallbackResourceGroupMapping.Length; ++i)
                        {
                            ResourceGroupPool[descriptorSetPoolOffset + fallbackResourceGroupMapping[i]] = renderEffect.FallbackParameterUpdater.ResourceGroups[i];
                        }
                    }

                    // Compile pipeline state object (if first time or need change)
                    // TODO GRAPHICS REFACTOR how to invalidate if we want to change some state? (setting to null should be fine)
                    if (renderEffect.PipelineState == null)
                    {
                        var mutablePipelineState = batch.MutablePipelineState;
                        var pipelineState = mutablePipelineState.State;
                        pipelineState.SetDefaults();

                        // Effect
                        pipelineState.EffectBytecode = renderEffect.Effect.Bytecode;
                        pipelineState.RootSignature = renderEffect.Reflection.RootSignature;

                        // Extract outputs from render stage
                        pipelineState.Output = renderNode.RenderStage.Output;
                        pipelineState.RasterizerState.MultiSampleLevel = renderNode.RenderStage.Output.MultiSampleLevel;

                        // Bind VAO
                        ProcessPipelineState(Context, renderNodeReference, ref renderNode, renderObject, pipelineState);

                        PostProcessPipelineState?.Invoke(renderNodeReference, ref renderNode, renderObject, pipelineState);

                        mutablePipelineState.Update();
                        renderEffect.PipelineState = mutablePipelineState.CurrentState;
                    }

                    RenderNodes[renderNodeReference.Index] = renderNode;
                });

                viewFeature.RenderNodes.Close();
                viewFeature.Layouts.Close();
            }

            EffectObjectNodes.Close();
            FrameLayouts.Close();
        }
Пример #2
0
        /// <inheritdoc/>
        public override void Prepare(RenderDrawContext context)
        {
            EffectObjectNodes.Clear(false);

            // Make sure descriptor set pool is large enough
            var expectedDescriptorSetPoolSize = RenderNodes.Count * effectDescriptorSetSlots.Count;

            if (ResourceGroupPool.Length < expectedDescriptorSetPoolSize)
            {
                Array.Resize(ref ResourceGroupPool, expectedDescriptorSetPoolSize);
            }

            // Allocate PerFrame, PerView and PerDraw resource groups and constant buffers
            var renderEffects   = RenderData.GetData(RenderEffectKey);
            int effectSlotCount = EffectPermutationSlotCount;

            foreach (var view in RenderSystem.Views)
            {
                var viewFeature = view.Features[Index];

                Dispatcher.ForEach(viewFeature.RenderNodes, () => prepareThreadContext.Value, (renderNodeReference, batch) =>
                {
                    var threadContext = batch.Context;
                    var renderNode    = this.GetRenderNode(renderNodeReference);
                    var renderObject  = renderNode.RenderObject;

                    // Get RenderEffect
                    var staticObjectNode       = renderObject.StaticObjectNode;
                    var staticEffectObjectNode = staticObjectNode * effectSlotCount + effectSlots[renderNode.RenderStage.Index].Index;
                    var renderEffect           = renderEffects[staticEffectObjectNode];

                    // Not compiled yet?
                    if (renderEffect.Effect == null)
                    {
                        renderNode.RenderEffect                = renderEffect;
                        renderNode.EffectObjectNode            = EffectObjectNodeReference.Invalid;
                        renderNode.Resources                   = null;
                        RenderNodes[renderNodeReference.Index] = renderNode;
                        return;
                    }

                    var renderEffectReflection = renderEffect.Reflection;

                    // PerView resources/cbuffer
                    var viewLayout = renderEffectReflection.PerViewLayout;
                    if (viewLayout != null)
                    {
                        var viewCount = RenderSystem.Views.Count;
                        if (viewLayout.Entries?.Length < viewCount)
                        {
                            // TODO: Should this be a first loop?
                            lock (viewLayout)
                            {
                                if (viewLayout.Entries?.Length < viewCount)
                                {
                                    var newEntries = new ResourceGroupEntry[viewCount];

                                    for (int index = 0; index < viewLayout.Entries.Length; index++)
                                    {
                                        newEntries[index] = viewLayout.Entries[index];
                                    }

                                    for (int index = viewLayout.Entries.Length; index < viewCount; index++)
                                    {
                                        newEntries[index].Resources = new ResourceGroup();
                                    }

                                    viewLayout.Entries = newEntries;
                                }
                            }
                        }

                        if (viewLayout.Entries[view.Index].MarkAsUsed(RenderSystem))
                        {
                            threadContext.ResourceGroupAllocator.PrepareResourceGroup(viewLayout, BufferPoolAllocationType.UsedMultipleTime, viewLayout.Entries[view.Index].Resources);

                            // Register it in list of view layouts to update for this frame
                            viewFeature.Layouts.Add(viewLayout);
                        }
                    }

                    // PerFrame resources/cbuffer
                    var frameLayout = renderEffect.Reflection.PerFrameLayout;
                    if (frameLayout != null && frameLayout.Entry.MarkAsUsed(RenderSystem))
                    {
                        threadContext.ResourceGroupAllocator.PrepareResourceGroup(frameLayout, BufferPoolAllocationType.UsedMultipleTime, frameLayout.Entry.Resources);

                        // Register it in list of view layouts to update for this frame
                        FrameLayouts.Add(frameLayout);
                    }

                    // PerDraw resources/cbuffer
                    // Get nodes
                    var viewObjectNode = GetViewObjectNode(renderNode.ViewObjectNode);

                    // Allocate descriptor set
                    renderNode.Resources = threadContext.ResourceGroupAllocator.AllocateResourceGroup();
                    if (renderEffectReflection.PerDrawLayout != null)
                    {
                        threadContext.ResourceGroupAllocator.PrepareResourceGroup(renderEffectReflection.PerDrawLayout, BufferPoolAllocationType.UsedOnce, renderNode.Resources);
                    }

                    // Create EffectObjectNode
                    var effectObjectNodeIndex = EffectObjectNodes.Add(new EffectObjectNode(renderEffect, viewObjectNode.ObjectNode));

                    // Link to EffectObjectNode (created right after)
                    // TODO: rewrite this
                    renderNode.EffectObjectNode = new EffectObjectNodeReference(effectObjectNodeIndex);

                    renderNode.RenderEffect = renderEffect;

                    // Bind well-known descriptor sets
                    var descriptorSetPoolOffset = ComputeResourceGroupOffset(renderNodeReference);
                    ResourceGroupPool[descriptorSetPoolOffset + perFrameDescriptorSetSlot.Index] = frameLayout?.Entry.Resources;
                    ResourceGroupPool[descriptorSetPoolOffset + perViewDescriptorSetSlot.Index]  = renderEffect.Reflection.PerViewLayout?.Entries[view.Index].Resources;
                    ResourceGroupPool[descriptorSetPoolOffset + perDrawDescriptorSetSlot.Index]  = renderNode.Resources;

                    // Create resource group for everything else in case of fallback effects
                    if (renderEffect.State != RenderEffectState.Normal && renderEffect.FallbackParameters != null)
                    {
                        if (renderEffect.FallbackParameterUpdater.ResourceGroups == null)
                        {
                            // First time
                            renderEffect.FallbackParameterUpdater = new EffectParameterUpdater(renderEffect.Reflection.FallbackUpdaterLayout, renderEffect.FallbackParameters);
                        }

                        renderEffect.FallbackParameterUpdater.Update(RenderSystem.GraphicsDevice, threadContext.ResourceGroupAllocator, renderEffect.FallbackParameters);

                        var fallbackResourceGroupMapping = renderEffect.Reflection.FallbackResourceGroupMapping;
                        for (int i = 0; i < fallbackResourceGroupMapping.Length; ++i)
                        {
                            ResourceGroupPool[descriptorSetPoolOffset + fallbackResourceGroupMapping[i]] = renderEffect.FallbackParameterUpdater.ResourceGroups[i];
                        }
                    }

                    // Compile pipeline state object (if first time or need change)
                    // TODO GRAPHICS REFACTOR how to invalidate if we want to change some state? (setting to null should be fine)
                    if (renderEffect.PipelineState == null)
                    {
                        var mutablePipelineState = batch.MutablePipelineState;
                        var pipelineState        = mutablePipelineState.State;
                        pipelineState.SetDefaults();

                        // Effect
                        pipelineState.EffectBytecode = renderEffect.Effect.Bytecode;
                        pipelineState.RootSignature  = renderEffect.Reflection.RootSignature;

                        // Extract outputs from render stage
                        pipelineState.Output = renderNode.RenderStage.Output;
                        pipelineState.RasterizerState.MultiSampleLevel = renderNode.RenderStage.Output.MultiSampleLevel;

                        // Bind VAO
                        ProcessPipelineState(Context, renderNodeReference, ref renderNode, renderObject, pipelineState);

                        PostProcessPipelineState?.Invoke(renderNodeReference, ref renderNode, renderObject, pipelineState);

                        mutablePipelineState.Update();
                        renderEffect.PipelineState = mutablePipelineState.CurrentState;
                    }

                    RenderNodes[renderNodeReference.Index] = renderNode;
                });

                viewFeature.RenderNodes.Close();
                viewFeature.Layouts.Close();
            }

            EffectObjectNodes.Close();
            FrameLayouts.Close();
        }