stride3d/stride · error · InvalidOperationException

Missing forward lighting render feature

Error message

Missing forward lighting render feature

What it means

After finding a MeshRenderFeature, LightShafts.InitializeCore also requires a ForwardLightingRenderFeature nested within it, because it needs that feature's ShadowMapRenderer to render light shafts. It throws InvalidOperationException when the ForwardLightingRenderFeature is absent. The pipeline meshes but uses a different lighting strategy (e.g. deferred), so light shafts cannot obtain shadow data.

Solutions

  1. Add a ForwardLightingRenderFeature as a child feature of the MeshRenderFeature in your RenderSystem configuration.
  2. If using deferred lighting, remove LightShafts or implement/port it for your lighting feature.
  3. Verify with code: meshRenderFeature.RenderFeatures.OfType<ForwardLightingRenderFeature>().Any() before enabling light shafts.

Example fix

// before
var meshFeature = new MeshRenderFeature(); // no lighting sub-feature
// after
var meshFeature = new MeshRenderFeature();
meshFeature.RenderFeatures.Add(new ForwardLightingRenderFeature());
Defensive patterns

Strategy: validation

Validate before calling

var mesh = Context.RenderSystem.RenderFeatures.OfType<MeshRenderFeature>().FirstOrDefault();
if (mesh == null || !mesh.RenderFeatures.OfType<ForwardLightingRenderFeature>().Any())
    throw new InvalidOperationException("LightShafts requires ForwardLightingRenderFeature under MeshRenderFeature");

Try / catch

try { EnableLightShafts(scene); }
catch (InvalidOperationException e) { Log.Warning($"Light shafts unavailable with current lighting pipeline: {e.Message}"); }

Prevention

When it happens

Trigger: A compositor whose MeshRenderFeature does not include ForwardLightingRenderFeature (deferred lighting pipeline or custom lighting features), while a LightShafts renderer is enabled in the scene.

Common situations: Switching a project from forward to deferred lighting and leaving light shafts enabled; custom lighting render features replacing the stock forward one.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Rendering/Rendering/Images/LightShafts/LightShafts.cs:96

            lightShaftsEffectShader = ToLoadAndUnload(new ImageEffectShader("LightShaftsEffect"));

            // Additive blending shader
            applyLightEffectShader = ToLoadAndUnload(new ImageEffectShader("AdditiveLightEffect"));
            applyLightEffectShader.BlendState = new BlendStateDescription(Blend.One, Blend.One);

            minmaxVolumeEffectShader = new DynamicEffectInstance("VolumeMinMaxShader");
            minmaxVolumeEffectShader.Initialize(Context.Services);

            blur = ToLoadAndUnload(new GaussianBlur());

            // Need the shadow map renderer in order to render light shafts
            var meshRenderFeature = Context.RenderSystem.RenderFeatures.OfType<MeshRenderFeature>().FirstOrDefault();
            if (meshRenderFeature == null)
                throw new InvalidOperationException("Missing mesh render feature");

            var forwardLightingFeature = meshRenderFeature.RenderFeatures.OfType<ForwardLightingRenderFeature>().FirstOrDefault();
            if (forwardLightingFeature == null)
                throw new InvalidOperationException("Missing forward lighting render feature");

            shadowMapRenderer = forwardLightingFeature.ShadowMapRenderer;

            for (int i = 0; i < 2; ++i)
            {
                var minmaxPipelineState = new MutablePipelineState(Context.GraphicsDevice);
                minmaxPipelineState.State.SetDefaults();

                minmaxPipelineState.State.BlendState.RenderTargets[0].BlendEnable = true;
                minmaxPipelineState.State.BlendState.RenderTargets[0].ColorSourceBlend = Blend.One;
                minmaxPipelineState.State.BlendState.RenderTargets[0].ColorDestinationBlend = Blend.One;
                minmaxPipelineState.State.BlendState.RenderTargets[0].ColorBlendFunction = i == 0 ? BlendFunction.Min : BlendFunction.Max;
                minmaxPipelineState.State.BlendState.RenderTargets[0].ColorWriteChannels = i == 0 ? ColorWriteChannels.Red : ColorWriteChannels.Green;

                minmaxPipelineState.State.DepthStencilState.DepthBufferEnable = false;
                minmaxPipelineState.State.DepthStencilState.DepthBufferWriteEnable = false;
                minmaxPipelineState.State.RasterizerState.DepthClipEnable = true;
                minmaxPipelineState.State.RasterizerState.CullMode = i == 0 ? CullMode.Back : CullMode.Front;

View on GitHub (pinned to 96fad776d2)