public EffectParameterUpdaterLayout(GraphicsDevice graphicsDevice, Effect effect, DescriptorSetLayoutBuilder[] layouts)
        {
            Layouts = layouts;

            // Process constant buffers
            ResourceGroupLayouts = new ResourceGroupLayout[layouts.Length];
            for (int layoutIndex = 0; layoutIndex < layouts.Length; layoutIndex++)
            {
                var layout = layouts[layoutIndex];
                if (layout == null)
                    continue;

                ParameterCollectionLayout.ProcessResources(layout);

                EffectConstantBufferDescription cbuffer = null;

                for (int entryIndex = 0; entryIndex < layout.Entries.Count; ++entryIndex)
                {
                    var layoutEntry = layout.Entries[entryIndex];
                    if (layoutEntry.Class == EffectParameterClass.ConstantBuffer)
                    {
                        // For now we assume first cbuffer will be the main one
                        if (cbuffer == null)
                        {
                            cbuffer = effect.Bytecode.Reflection.ConstantBuffers.First(x => x.Name == layoutEntry.Key.Name);
                            ParameterCollectionLayout.ProcessConstantBuffer(cbuffer);
                        }
                    }
                }

                var resourceGroupDescription = new ResourceGroupDescription(layout, cbuffer);

                ResourceGroupLayouts[layoutIndex] = ResourceGroupLayout.New(graphicsDevice, resourceGroupDescription, effect.Bytecode);
            }
        }
        public static EffectDescriptorSetReflection New(GraphicsDevice graphicsDevice, EffectBytecode effectBytecode, List<string> effectDescriptorSetSlots, string defaultSetSlot)
        {
            // Find resource groups
            // TODO: We should precompute most of that at compile time in BytecodeReflection
            // just waiting for format to be more stable
            var descriptorSetLayouts = new EffectDescriptorSetReflection { DefaultSetSlot = defaultSetSlot };
            foreach (var effectDescriptorSetSlot in effectDescriptorSetSlots)
            {
                // Find all resources related to this slot name
                // NOTE: Ordering is mirrored by GLSL layout in Vulkan
                var descriptorSetLayoutBuilder = new DescriptorSetLayoutBuilder();
                bool hasBindings = false;
                foreach (var resourceBinding in effectBytecode.Reflection.ResourceBindings
                    .Where(x => x.ResourceGroup == effectDescriptorSetSlot || (effectDescriptorSetSlot == defaultSetSlot && (x.ResourceGroup == null || x.ResourceGroup == "Globals")))
                    .GroupBy(x => new { Key = x.KeyInfo.Key, Class = x.Class, Type = x.Type, SlotCount = x.SlotCount, LogicalGroup = x.LogicalGroup })
                    .OrderBy(x => x.Key.Class == EffectParameterClass.ConstantBuffer ? 0 : 1)) // Note: Putting cbuffer first for now
                {
                    SamplerState samplerState = null;
                    if (resourceBinding.Key.Class == EffectParameterClass.Sampler)
                    {
                        var matchingSamplerState = effectBytecode.Reflection.SamplerStates.FirstOrDefault(x => x.Key == resourceBinding.Key.Key);
                        if (matchingSamplerState != null)
                            samplerState = SamplerState.New(graphicsDevice, matchingSamplerState.Description);
                    }
                    hasBindings = true;

                    descriptorSetLayoutBuilder.AddBinding(resourceBinding.Key.Key, resourceBinding.Key.LogicalGroup, resourceBinding.Key.Class, resourceBinding.Key.Type, resourceBinding.Key.SlotCount, samplerState);
                }

                descriptorSetLayouts.AddLayout(effectDescriptorSetSlot, hasBindings ? descriptorSetLayoutBuilder : null);
            }

            return descriptorSetLayouts;
        }
 public static void ProcessResources(this ParameterCollectionLayout parameterCollectionLayout, DescriptorSetLayoutBuilder layout)
 {
     foreach (var layoutEntry in layout.Entries)
     {
         parameterCollectionLayout.LayoutParameterKeyInfos.Add(new ParameterKeyInfo(layoutEntry.Key, parameterCollectionLayout.ResourceCount++));
     }
 }
        /// <summary>
        /// Compiles or recompiles the effect if necesssary.
        /// </summary>
        /// <param name="graphicsDevice"></param>
        /// <returns>True if the effect was recompiled, false otherwise.</returns>
        public bool UpdateEffect(GraphicsDevice graphicsDevice)
        {
            if (permutationCounter != Parameters.PermutationCounter || (effect != null && effect.SourceChanged))
            {
                permutationCounter = Parameters.PermutationCounter;

                var oldEffect = effect;
                ChooseEffect(graphicsDevice);

                // Early exit: same effect, and already initialized
                if (oldEffect == effect && descriptorReflection != null)
                    return false;

                // Update reflection and rearrange buffers/resources
                var layoutNames = effect.Bytecode.Reflection.ResourceBindings.Select(x => x.ResourceGroup ?? "Globals").Distinct().ToList();
                descriptorReflection = EffectDescriptorSetReflection.New(graphicsDevice, effect.Bytecode, layoutNames, "Globals");

                RootSignature = RootSignature.New(graphicsDevice, descriptorReflection);

                bufferUploader.Compile(graphicsDevice, descriptorReflection, effect.Bytecode);

                // Create parameter updater
                var layouts = new DescriptorSetLayoutBuilder[descriptorReflection.Layouts.Count];
                for (int i = 0; i < descriptorReflection.Layouts.Count; ++i)
                    layouts[i] = descriptorReflection.Layouts[i].Layout;
                var parameterUpdaterLayout = new EffectParameterUpdaterLayout(graphicsDevice, effect, layouts);
                parameterUpdater = new EffectParameterUpdater(parameterUpdaterLayout, Parameters);

                descriptorSets = new DescriptorSet[parameterUpdater.ResourceGroups.Length];

                return true;
            }

            return false;
        }
Example #5
0
        public static EffectDescriptorSetReflection New(GraphicsDevice graphicsDevice, EffectBytecode effectBytecode, List <string> effectDescriptorSetSlots, string defaultSetSlot)
        {
            // Find resource groups
            // TODO: We should precompute most of that at compile time in BytecodeReflection
            // just waiting for format to be more stable
            var descriptorSetLayouts = new EffectDescriptorSetReflection();

            foreach (var effectDescriptorSetSlot in effectDescriptorSetSlots)
            {
                // Find all resources related to this slot name
                var  descriptorSetLayoutBuilder = new DescriptorSetLayoutBuilder();
                bool hasBindings = false;
                foreach (var resourceBinding in effectBytecode.Reflection.ResourceBindings
                         .Where(x => x.Param.ResourceGroup == effectDescriptorSetSlot || (effectDescriptorSetSlot == defaultSetSlot && (x.Param.ResourceGroup == null || x.Param.ResourceGroup == "Globals")))
                         .GroupBy(x => new { Key = x.Param.Key, Class = x.Param.Class, SlotCount = x.SlotCount })
                         .OrderBy(x => x.Key.Class == EffectParameterClass.ConstantBuffer ? 0 : 1)) // Note: Putting cbuffer first for now
                {
                    SamplerState samplerState = null;
                    if (resourceBinding.Key.Class == EffectParameterClass.Sampler)
                    {
                        var matchingSamplerState = effectBytecode.Reflection.SamplerStates.FirstOrDefault(x => x.Key == resourceBinding.Key.Key);
                        if (matchingSamplerState != null)
                        {
                            samplerState = SamplerState.New(graphicsDevice, matchingSamplerState.Description);
                        }
                    }
                    hasBindings = true;
                    descriptorSetLayoutBuilder.AddBinding(resourceBinding.Key.Key, resourceBinding.Key.Class, resourceBinding.Key.SlotCount, samplerState);
                }

                descriptorSetLayouts.AddLayout(effectDescriptorSetSlot, hasBindings ? descriptorSetLayoutBuilder : null);
            }

            return(descriptorSetLayouts);
        }
Example #6
0
        public ResourceGroupDescription(DescriptorSetLayoutBuilder descriptorSetLayout, EffectConstantBufferDescription constantBufferReflection) : this()
        {
            DescriptorSetLayout      = descriptorSetLayout;
            ConstantBufferReflection = constantBufferReflection;

            // We combine both hash for DescriptorSet and cbuffer itself (if it exists)
            Hash = descriptorSetLayout.Hash;
            if (constantBufferReflection != null)
            {
                ObjectId.Combine(ref Hash, ref constantBufferReflection.Hash, out Hash);
            }
        }
 private DescriptorSetLayout(GraphicsDevice device, DescriptorSetLayoutBuilder builder)
 {
     BindingOffsets = new int[builder.ElementCount];
     int currentBindingOffset = 0;
     foreach (var entry in builder.Entries)
     {
         // We will both setup BindingOffsets and increment SamplerCount/SrvCount at the same time
         if (entry.Class == EffectParameterClass.Sampler)
         {
             for (int i = 0; i < entry.ArraySize; ++i)
                 BindingOffsets[currentBindingOffset++] = entry.ImmutableSampler != null ? -1 : SamplerCount++ * device.SamplerHandleIncrementSize;
         }
         else
         {
             for (int i = 0; i < entry.ArraySize; ++i)
                 BindingOffsets[currentBindingOffset++] = SrvCount++ * device.SrvHandleIncrementSize;
         }
     }
 }
        private DescriptorSetLayout(GraphicsDevice device, DescriptorSetLayoutBuilder builder)
        {
            BindingOffsets = new int[builder.ElementCount];
            int currentBindingOffset = 0;

            foreach (var entry in builder.Entries)
            {
                // We will both setup BindingOffsets and increment SamplerCount/SrvCount at the same time
                if (entry.Class == EffectParameterClass.Sampler)
                {
                    for (int i = 0; i < entry.ArraySize; ++i)
                    {
                        BindingOffsets[currentBindingOffset++] = entry.ImmutableSampler != null ? -1 : SamplerCount++ *device.SamplerHandleIncrementSize;
                    }
                }
                else
                {
                    for (int i = 0; i < entry.ArraySize; ++i)
                    {
                        BindingOffsets[currentBindingOffset++] = SrvCount++ *device.SrvHandleIncrementSize;
                    }
                }
            }
        }
 private DescriptorSetLayout(GraphicsDevice device, DescriptorSetLayoutBuilder builder) : base(device)
 {
     this.Builder = builder;
     Recreate();
 }
Example #10
0
 private DescriptorSetLayout(GraphicsDevice device, DescriptorSetLayoutBuilder builder) : base(device)
 {
     this.Builder = builder;
     Recreate();
 }
 public static DescriptorSetLayout New(GraphicsDevice device, DescriptorSetLayoutBuilder builder)
 {
     return(new DescriptorSetLayout(device, builder));
 }
 private DescriptorSetLayout(GraphicsDevice device, DescriptorSetLayoutBuilder builder)
 {
     ElementCount = builder.ElementCount;
     Entries      = builder.Entries.ToArray();
 }
 public void AddLayout(string descriptorSetName, DescriptorSetLayoutBuilder descriptorSetLayoutBuilder)
 {
     Layouts.Add(new LayoutEntry(descriptorSetName, descriptorSetLayoutBuilder));
 }
        public DescriptorSetLayout CreateUniqueDescriptorSetLayout(DescriptorSetLayoutBuilder descriptorSetLayoutBuilder)
        {
            DescriptorSetLayout descriptorSetLayout;

            if (!createdDescriptorSetLayouts.TryGetValue(descriptorSetLayoutBuilder.Hash, out descriptorSetLayout))
            {
                descriptorSetLayout = DescriptorSetLayout.New(RenderSystem.GraphicsDevice, descriptorSetLayoutBuilder);
                createdDescriptorSetLayouts.Add(descriptorSetLayoutBuilder.Hash, descriptorSetLayout);
            }

            return descriptorSetLayout;
        }
 private DescriptorSetLayout(GraphicsDevice device, DescriptorSetLayoutBuilder builder)
 {
     ElementCount = builder.ElementCount;
     Entries = builder.Entries.ToArray();
 }
 public LayoutEntry(string name, DescriptorSetLayoutBuilder layout)
 {
     Name = name;
     Layout = layout;
 }
Example #17
0
 public void AddLayout(string descriptorSetName, DescriptorSetLayoutBuilder descriptorSetLayoutBuilder)
 {
     Layouts.Add(new LayoutEntry(descriptorSetName, descriptorSetLayoutBuilder));
 }
Example #18
0
 public LayoutEntry(string name, DescriptorSetLayoutBuilder layout)
 {
     Name   = name;
     Layout = layout;
 }
        /// <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;
                    }
                }
            }
        }
Example #20
0
 public static void ProcessResources(this ParameterCollectionLayout parameterCollectionLayout, DescriptorSetLayoutBuilder layout)
 {
     foreach (var layoutEntry in layout.Entries)
     {
         parameterCollectionLayout.LayoutParameterKeyInfos.Add(new ParameterKeyInfo(layoutEntry.Key, parameterCollectionLayout.ResourceCount++));
     }
 }
 public static DescriptorSetLayout New(GraphicsDevice device, DescriptorSetLayoutBuilder builder)
 {
     return new DescriptorSetLayout(device, builder);
 }