stride3d/stride · error · InvalidOperationException

Only 32 effect slots are currently allowed for meshes

Error message

Only 32 effect slots are currently allowed for meshes

What it means

RootEffectRenderFeature maps each effect (by name) to a bit-flag permutation slot stored in a 32-bit mask (effectPermutationSlots). Registering a 33rd distinct effect would overflow the slot encoding, so CreateEffectPermutationSlot throws InvalidOperationException instead of corrupting render state selection.

Solutions

  1. Reduce the number of distinct effect permutations: merge variants into a single effect with a parameter/displacement instead of separate effect names
  2. Split your objects across multiple RenderFeatures/RenderStages so each feature stays under 32 slots
  3. Audit dynamically added render stages/features that call CreateEffectPermutationSlot at runtime and remove unused ones
  4. Reuse existing effect slots by passing effect names already registered (effectPermutationSlots dedupes by name)

Example fix

// before
foreach (var effectName in allEffectNames) // > 32 names
    feature.CreateEffectPermutationSlot(effectName);
// after
foreach (var effectName in allEffectNames.Take(32))
    feature.CreateEffectPermutationSlot(effectName);
// move remaining effects to a second render feature
Defensive patterns

Strategy: validation

Validate before calling

if (effectNames.Distinct().Count() > 32)
    throw new InvalidOperationException("Split effects across multiple render features (max 32 slots).");

Try / catch

try
{
    feature.CreateEffectPermutationSlot(effectName);
}
catch (InvalidOperationException)
{
    Logger.Warning("Effect slot limit reached; merging effect variants");
}

Prevention

When it happens

Trigger: Calling CreateEffectPermutationSlot with more than 32 distinct effect names on a single RootEffectRenderFeature — e.g. many MaterialPass/effect variations registered dynamically, or RenderStages collection changes (RenderStages_CollectionChanged) adding slots repeatedly at runtime.

Common situations: Large projects with dozens of material effect permutations on one render feature; runtime-added render stages each registering their own effect; forgetting that slot allocation is per-feature, not global.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/f98a06d440af26ff. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Rendering/Rendering/RootEffectRenderFeature.cs:372

        {
            return effectSlots[renderStage.Index];
        }

        /// <summary>
        /// Creates a slot for storing a particular effect instantiation (per RenderObject).
        /// </summary>
        /// As an example, we could have main shader (automatically created), GBuffer shader and shadow mapping shader.
        /// <returns></returns>
        public EffectPermutationSlot CreateEffectPermutationSlot(string effectName)
        {
            // Allocate effect slot
            // TODO: Should we allow/support this to be called after Initialize()?
            int slot;
            if (!effectPermutationSlots.TryGetValue(effectName, out slot))
            {
                if (effectPermutationSlots.Count >= 32)
                {
                    throw new InvalidOperationException("Only 32 effect slots are currently allowed for meshes");
                }

                slot = effectPermutationSlots.Count;
                effectPermutationSlots.Add(effectName, slot);

                // Add render effect slot
                RenderData.ChangeDataMultiplier(RenderEffectKey, EffectPermutationSlotCount);
            }

            return new EffectPermutationSlot(slot);
        }

        /// <summary>
        /// This is a subpart of effect permutation preparation:
        ///  set the shader classes that are going to be responsible to compute extended render target colors.
        /// </summary>
        /// <param name="context"></param>
        private void PrepareRenderTargetExtensionsMixins(RenderDrawContext context)

View on GitHub (pinned to 96fad776d2)