Exemple #1
0
        /// <param name="context"></param>
        /// <inheritdoc/>
        public override void PrepareEffectPermutations(RenderDrawContext context)
        {
            base.PrepareEffectPermutations(context);

            // TODO: Temporary until we have a better system for handling permutations
            var renderEffects   = RenderData.GetData(RenderEffectKey);
            int effectSlotCount = EffectPermutationSlotCount;

            foreach (var view in RenderSystem.Views)
            {
                var viewFeature = view.Features[Index];
                foreach (var renderNodeReference in viewFeature.RenderNodes)
                {
                    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];

                    var effectSelector = renderObject.ActiveRenderStages[renderNode.RenderStage.Index].EffectSelector;

                    // Create it (first time) or regenerate it if effect changed
                    if (renderEffect == null || effectSelector != renderEffect.EffectSelector)
                    {
                        renderEffect = new RenderEffect(renderObject.ActiveRenderStages[renderNode.RenderStage.Index].EffectSelector);
                        renderEffects[staticEffectObjectNode] = renderEffect;
                    }

                    // Is it the first time this frame that we check this RenderEffect?
                    if (renderEffect.MarkAsUsed(RenderSystem))
                    {
                        renderEffect.EffectValidator.BeginEffectValidation();
                    }
                }
            }

            // Step1: Perform permutations
            PrepareEffectPermutationsImpl(context);

            // CompilerParameters are ThreadStatic
            if (staticCompilerParameters == null)
            {
                staticCompilerParameters = new CompilerParameters();
            }

            // Step2: Compile effects and update reflection infos (offset, etc...)
            foreach (var renderObject in RenderObjects)
            {
                var staticObjectNode = renderObject.StaticObjectNode;

                for (int i = 0; i < effectSlotCount; ++i)
                {
                    var staticEffectObjectNode = staticObjectNode * effectSlotCount + i;
                    var renderEffect           = renderEffects[staticEffectObjectNode];

                    // Skip if not used
                    if (renderEffect == null || !renderEffect.IsUsedDuringThisFrame(RenderSystem))
                    {
                        continue;
                    }

                    // Skip if nothing changed
                    Effect effect;
                    if (renderEffect.EffectValidator.ShouldSkip)
                    {
                        // Reset pending effect, as it is now obsolete anyway
                        effect = null;
                        renderEffect.Effect = null;
                        renderEffect.State  = RenderEffectState.Skip;
                    }
                    else if (renderEffect.EffectValidator.EndEffectValidation() && (renderEffect.Effect == null || !renderEffect.Effect.SourceChanged))
                    {
                        InvalidateEffectPermutation(renderObject, renderEffect);

                        // Still, let's check if there is a pending effect compiling
                        var pendingEffect = renderEffect.PendingEffect;
                        if (pendingEffect == null || !pendingEffect.IsCompleted)
                        {
                            continue;
                        }

                        renderEffect.ClearFallbackParameters();
                        if (pendingEffect.IsFaulted)
                        {
                            renderEffect.State = RenderEffectState.Error;
                            effect             = ComputeFallbackEffect?.Invoke(renderObject, renderEffect, RenderEffectState.Error);
                        }
                        else
                        {
                            renderEffect.State = RenderEffectState.Normal;
                            effect             = pendingEffect.Result;
                        }
                        renderEffect.PendingEffect = null;
                    }
                    else
                    {
                        // Reset pending effect, as it is now obsolete anyway
                        renderEffect.PendingEffect = null;
                        renderEffect.State         = RenderEffectState.Normal;

                        foreach (var effectValue in renderEffect.EffectValidator.EffectValues)
                        {
                            staticCompilerParameters.SetObject(effectValue.Key, effectValue.Value);
                        }

                        var asyncEffect = RenderSystem.EffectSystem.LoadEffect(renderEffect.EffectSelector.EffectName, staticCompilerParameters);
                        staticCompilerParameters.Clear();

                        effect = asyncEffect.Result;
                        if (effect == null)
                        {
                            // Effect still compiling, let's find if there is a fallback
                            renderEffect.ClearFallbackParameters();
                            effect = ComputeFallbackEffect?.Invoke(renderObject, renderEffect, RenderEffectState.Compiling);
                            if (effect != null)
                            {
                                // Use the fallback for now
                                renderEffect.PendingEffect = asyncEffect.Task;
                                renderEffect.State         = RenderEffectState.Compiling;
                            }
                            else
                            {
                                // No fallback effect, let's block until effect is compiled
                                effect = asyncEffect.WaitForResult();
                            }
                        }
                    }

                    var effectHashCode = effect != null ? (uint)effect.GetHashCode() : 0;

                    // Effect is last 16 bits
                    renderObject.StateSortKey = (renderObject.StateSortKey & 0xFFFF0000) | (effectHashCode & 0x0000FFFF);

                    if (effect != null)
                    {
                        RenderEffectReflection renderEffectReflection;
                        if (!InstantiatedEffects.TryGetValue(effect, out renderEffectReflection))
                        {
                            renderEffectReflection = new RenderEffectReflection();

                            // Build root signature automatically from reflection
                            renderEffectReflection.DescriptorReflection      = EffectDescriptorSetReflection.New(RenderSystem.GraphicsDevice, effect.Bytecode, effectDescriptorSetSlots, "PerFrame");
                            renderEffectReflection.ResourceGroupDescriptions = new ResourceGroupDescription[renderEffectReflection.DescriptorReflection.Layouts.Count];

                            // Compute ResourceGroup hashes
                            for (int index = 0; index < renderEffectReflection.DescriptorReflection.Layouts.Count; index++)
                            {
                                var descriptorSet = renderEffectReflection.DescriptorReflection.Layouts[index];
                                if (descriptorSet.Layout == null)
                                {
                                    continue;
                                }

                                var constantBufferReflection = effect.Bytecode.Reflection.ConstantBuffers.FirstOrDefault(x => x.Name == descriptorSet.Name);

                                renderEffectReflection.ResourceGroupDescriptions[index] = new ResourceGroupDescription(descriptorSet.Layout, constantBufferReflection);
                            }

                            renderEffectReflection.RootSignature = RootSignature.New(RenderSystem.GraphicsDevice, renderEffectReflection.DescriptorReflection);
                            renderEffectReflection.BufferUploader.Compile(RenderSystem.GraphicsDevice, renderEffectReflection.DescriptorReflection, effect.Bytecode);

                            // Prepare well-known descriptor set layouts
                            renderEffectReflection.PerDrawLayout  = CreateDrawResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perDrawDescriptorSetSlot.Index], effect.Bytecode);
                            renderEffectReflection.PerFrameLayout = CreateFrameResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perFrameDescriptorSetSlot.Index], effect.Bytecode);
                            renderEffectReflection.PerViewLayout  = CreateViewResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perViewDescriptorSetSlot.Index], effect.Bytecode);

                            InstantiatedEffects.Add(effect, renderEffectReflection);

                            // Notify a new effect has been compiled
                            EffectCompiled?.Invoke(RenderSystem, effect, renderEffectReflection);
                        }

                        // Setup fallback parameters
                        if (renderEffect.State != RenderEffectState.Normal && renderEffectReflection.FallbackUpdaterLayout == null)
                        {
                            // Process all "non standard" layouts
                            var layoutMapping      = new int[renderEffectReflection.DescriptorReflection.Layouts.Count - 3];
                            var layouts            = new DescriptorSetLayoutBuilder[renderEffectReflection.DescriptorReflection.Layouts.Count - 3];
                            int layoutMappingIndex = 0;
                            for (int index = 0; index < renderEffectReflection.DescriptorReflection.Layouts.Count; index++)
                            {
                                var layout = renderEffectReflection.DescriptorReflection.Layouts[index];

                                // Skip well-known layouts (already handled)
                                if (layout.Name == "PerDraw" || layout.Name == "PerFrame" || layout.Name == "PerView")
                                {
                                    continue;
                                }

                                layouts[layoutMappingIndex]         = layout.Layout;
                                layoutMapping[layoutMappingIndex++] = index;
                            }

                            renderEffectReflection.FallbackUpdaterLayout        = new EffectParameterUpdaterLayout(RenderSystem.GraphicsDevice, effect, layouts);
                            renderEffectReflection.FallbackResourceGroupMapping = layoutMapping;
                        }

                        // Update effect
                        renderEffect.Effect     = effect;
                        renderEffect.Reflection = renderEffectReflection;

                        // Invalidate pipeline state (new effect)
                        renderEffect.PipelineState = null;

                        renderEffects[staticEffectObjectNode] = renderEffect;
                    }
                    else
                    {
                        renderEffect.Reflection    = RenderEffectReflection.Empty;
                        renderEffect.PipelineState = null;
                    }
                }
            }
        }
        /// <param name="context"></param>
        /// <inheritdoc/>
        public override void PrepareEffectPermutations(RenderDrawContext context)
        {
            base.PrepareEffectPermutations(context);

            // TODO: Temporary until we have a better system for handling permutations
            var renderEffects = RenderData.GetData(RenderEffectKey);
            int effectSlotCount = EffectPermutationSlotCount;

            Dispatcher.ForEach(RenderSystem.Views, view =>
            {
                var viewFeature = view.Features[Index];
                Dispatcher.ForEach(viewFeature.RenderNodes, renderNodeReference =>
                {
                    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];

                    var effectSelector = renderObject.ActiveRenderStages[renderNode.RenderStage.Index].EffectSelector;

                    // Create it (first time) or regenerate it if effect changed
                    if (renderEffect == null || effectSelector != renderEffect.EffectSelector)
                    {
                        renderEffect = new RenderEffect(renderObject.ActiveRenderStages[renderNode.RenderStage.Index].EffectSelector);
                        renderEffects[staticEffectObjectNode] = renderEffect;
                    }

                    // Is it the first time this frame that we check this RenderEffect?
                    if (renderEffect.MarkAsUsed(RenderSystem))
                    {
                        renderEffect.EffectValidator.BeginEffectValidation();
                    }
                });
            });

            // Step1: Perform permutations
            PrepareEffectPermutationsImpl(context);

            // Step2: Compile effects
            Dispatcher.ForEach(RenderObjects, renderObject =>
            {
                //var renderObject = RenderObjects[index];
                var staticObjectNode = renderObject.StaticObjectNode;

                for (int i = 0; i < effectSlotCount; ++i)
                {
                    var staticEffectObjectNode = staticObjectNode * effectSlotCount + i;
                    var renderEffect = renderEffects[staticEffectObjectNode];

                    // Skip if not used
                    if (renderEffect == null)
                        continue;

                    // Skip reflection update unless a state change requires it
                    renderEffect.IsReflectionUpdateRequired = false;

                    if (!renderEffect.IsUsedDuringThisFrame(RenderSystem))
                        continue;

                    // Skip if nothing changed
                    if (renderEffect.EffectValidator.ShouldSkip)
                    {
                        // Reset pending effect, as it is now obsolete anyway
                        renderEffect.Effect = null;
                        renderEffect.State = RenderEffectState.Skip;
                    }
                    else if (renderEffect.EffectValidator.EndEffectValidation() && (renderEffect.Effect == null || !renderEffect.Effect.SourceChanged))
                    {
                        InvalidateEffectPermutation(renderObject, renderEffect);

                        // Still, let's check if there is a pending effect compiling
                        var pendingEffect = renderEffect.PendingEffect;
                        if (pendingEffect == null || !pendingEffect.IsCompleted)
                            continue;

                        renderEffect.ClearFallbackParameters();
                        if (pendingEffect.IsFaulted)
                        {
                            renderEffect.State = RenderEffectState.Error;
                            renderEffect.Effect = ComputeFallbackEffect?.Invoke(renderObject, renderEffect, RenderEffectState.Error);
                        }
                        else
                        {
                            renderEffect.State = RenderEffectState.Normal;
                            renderEffect.Effect = pendingEffect.Result;
                        }
                        renderEffect.PendingEffect = null;
                    }
                    else
                    {
                        // Reset pending effect, as it is now obsolete anyway
                        renderEffect.PendingEffect = null;
                        renderEffect.State = RenderEffectState.Normal;

                        // CompilerParameters are ThreadStatic
                        if (staticCompilerParameters == null)
                            staticCompilerParameters = new CompilerParameters();

                        foreach (var effectValue in renderEffect.EffectValidator.EffectValues)
                        {
                            staticCompilerParameters.SetObject(effectValue.Key, effectValue.Value);
                        }

                        var asyncEffect = RenderSystem.EffectSystem.LoadEffect(renderEffect.EffectSelector.EffectName, staticCompilerParameters);
                        staticCompilerParameters.Clear();

                        renderEffect.Effect = asyncEffect.Result;
                        if (renderEffect.Effect == null)
                        {
                            // Effect still compiling, let's find if there is a fallback
                            renderEffect.ClearFallbackParameters();
                            renderEffect.Effect = ComputeFallbackEffect?.Invoke(renderObject, renderEffect, RenderEffectState.Compiling);
                            renderEffect.PendingEffect = asyncEffect.Task;
                            renderEffect.State = RenderEffectState.Compiling;
                        }
                    }

                    renderEffect.IsReflectionUpdateRequired = true;
                }
            });

            // Step3: Uupdate reflection infos (offset, etc...)
            foreach (var renderObject in RenderObjects)
            {
                var staticObjectNode = renderObject.StaticObjectNode;

                for (int i = 0; i < effectSlotCount; ++i)
                {
                    var staticEffectObjectNode = staticObjectNode * effectSlotCount + i;
                    var renderEffect = renderEffects[staticEffectObjectNode];

                    // Skip if not used
                    if (renderEffect == null || !renderEffect.IsReflectionUpdateRequired)
                        continue;

                    var effect = renderEffect.Effect;
                    if (effect == null && renderEffect.State == RenderEffectState.Compiling)
                    {
                        // Need to wait for completion
                        renderEffect.Effect = effect = renderEffect.PendingEffect.Result;
                        renderEffect.State = RenderEffectState.Normal;
                    }

                    var effectHashCode = effect != null ? (uint)effect.GetHashCode() : 0;

                    // Effect is last 16 bits
                    renderObject.StateSortKey = (renderObject.StateSortKey & 0xFFFF0000) | (effectHashCode & 0x0000FFFF);

                    if (effect != null)
                    {
                        RenderEffectReflection renderEffectReflection;
                        if (!InstantiatedEffects.TryGetValue(effect, out renderEffectReflection))
                        {
                            renderEffectReflection = new RenderEffectReflection();

                            // Build root signature automatically from reflection
                            renderEffectReflection.DescriptorReflection = EffectDescriptorSetReflection.New(RenderSystem.GraphicsDevice, effect.Bytecode, effectDescriptorSetSlots, "PerFrame");
                            renderEffectReflection.ResourceGroupDescriptions = new ResourceGroupDescription[renderEffectReflection.DescriptorReflection.Layouts.Count];

                            // Compute ResourceGroup hashes
                            for (int index = 0; index < renderEffectReflection.DescriptorReflection.Layouts.Count; index++)
                            {
                                var descriptorSet = renderEffectReflection.DescriptorReflection.Layouts[index];
                                if (descriptorSet.Layout == null)
                                    continue;

                                var constantBufferReflection = effect.Bytecode.Reflection.ConstantBuffers.FirstOrDefault(x => x.Name == descriptorSet.Name);

                                renderEffectReflection.ResourceGroupDescriptions[index] = new ResourceGroupDescription(descriptorSet.Layout, constantBufferReflection);
                            }

                            renderEffectReflection.RootSignature = RootSignature.New(RenderSystem.GraphicsDevice, renderEffectReflection.DescriptorReflection);
                            renderEffectReflection.BufferUploader.Compile(RenderSystem.GraphicsDevice, renderEffectReflection.DescriptorReflection, effect.Bytecode);

                            // Prepare well-known descriptor set layouts
                            renderEffectReflection.PerDrawLayout = CreateDrawResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perDrawDescriptorSetSlot.Index], renderEffect.State);
                            renderEffectReflection.PerFrameLayout = CreateFrameResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perFrameDescriptorSetSlot.Index], renderEffect.State);
                            renderEffectReflection.PerViewLayout = CreateViewResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perViewDescriptorSetSlot.Index], renderEffect.State);

                            InstantiatedEffects.Add(effect, renderEffectReflection);

                            // Notify a new effect has been compiled
                            EffectCompiled?.Invoke(RenderSystem, effect, renderEffectReflection);
                        }

                        // Setup fallback parameters
                        if (renderEffect.State != RenderEffectState.Normal && renderEffectReflection.FallbackUpdaterLayout == null)
                        {
                            // Process all "non standard" layouts
                            var layoutMapping = new int[renderEffectReflection.DescriptorReflection.Layouts.Count - 3];
                            var layouts = new DescriptorSetLayoutBuilder[renderEffectReflection.DescriptorReflection.Layouts.Count - 3];
                            int layoutMappingIndex = 0;
                            for (int index = 0; index < renderEffectReflection.DescriptorReflection.Layouts.Count; index++)
                            {
                                var layout = renderEffectReflection.DescriptorReflection.Layouts[index];

                                // Skip well-known layouts (already handled)
                                if (layout.Name == "PerDraw" || layout.Name == "PerFrame" || layout.Name == "PerView")
                                    continue;

                                layouts[layoutMappingIndex] = layout.Layout;
                                layoutMapping[layoutMappingIndex++] = index;
                            }

                            renderEffectReflection.FallbackUpdaterLayout = new EffectParameterUpdaterLayout(RenderSystem.GraphicsDevice, effect, layouts);
                            renderEffectReflection.FallbackResourceGroupMapping = layoutMapping;
                        }

                        // Update effect
                        renderEffect.Effect = effect;
                        renderEffect.Reflection = renderEffectReflection;

                        // Invalidate pipeline state (new effect)
                        renderEffect.PipelineState = null;

                        renderEffects[staticEffectObjectNode] = renderEffect;
                    }
                    else
                    {
                        renderEffect.Reflection = RenderEffectReflection.Empty;
                        renderEffect.PipelineState = null;
                    }
                }
            }
        }