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; }
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); }
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(); }
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; }
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; } } } }
public static DescriptorSetLayout New(GraphicsDevice device, DescriptorSetLayoutBuilder builder) { return new DescriptorSetLayout(device, builder); }