Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new CanvasEffect.EffectGraph APIs #517

Merged
merged 9 commits into from
May 1, 2023
Merged

Conversation

Sergio0694
Copy link
Owner

@Sergio0694 Sergio0694 commented Apr 30, 2023

Description

This PR adds new effect graph APIs to CanvasEffect. This enables authors of packaged effects to have a much more compact and safe way to build effect graphs, without the need to suppress nullability warnings when accessing effects from fields nor to manually override Dispose(bool) and then dispose every single field, which was pretty error prone as well.

API breakdown

namespace ComputeSharp.D2D1.Uwp; // Or .WinUI

public abstract class CanvasEffect
{
    protected abstract void BuildEffectGraph(EffectGraph effectGraph);
    protected abstract void ConfigureEffectGraph(EffectGraph effectGraph);

    protected readonly ref struct EffectGraph
    {
        public ICanvasImage GetNode(IEffectNode effectNode);
        public T GetNode<T>(IEffectNode<T> effectNode) where T : class, ICanvasImage;
        public void RegisterNode(ICanvasImage canvasImage);
        public void RegisterNode<T>(EffectNode<T> effectNode, T canvasImage) where T : class, ICanvasImage;
        public void RegisterOutputNode(ICanvasImage canvasImage);        
        public void RegisterOutputNode<T>(EffectNode<T> effectNode, T canvasImage) where T : class, ICanvasImage;
        public void SetOutputNode(IEffectNode effectNode);
    }

    protected interface IEffectNode
    {
    }

    protected interface IEffectNode<out T> : IEffectNode
        where T : ICanvasImage
    {
    }

    protected sealed class EffectNode<T>
        where T : class, ICanvasImage
    {
    }
}

Usage example

Here's how the new APIs can be used to define a custom effect, for instance, a "frosted glass" effect. This showcases how the "packaged" effect can build its own internal graph, register nodes, and then reference them when configuring the graph:

public sealed class FrostedGlassEffect : CanvasEffect
{
    private static readonly EffectNode<GaussianBlurEffect> BlurNode = new();
    private static readonly EffectNode<TintEffect> TintNode = new();
    private static readonly EffectNode<PixelShaderEffect<NoiseShader>> NoiseNode = new();

    private IGraphicsEffectSource? _source;

    public IGraphicsEffectSource? Source
    {
        get => _source;
        set => SetAndInvalidateEffectGraph(ref _source, value);
    }

    private float _blurAmount;

    public float BlurAmount
    {
        get => _blurAmount;
        set => SetAndInvalidateEffectGraph(ref _blurAmount, value);
    }

    private Color _tintColor;

    public Color TintColor
    {
        get => _tintColor;
        set => SetAndInvalidateEffectGraph(ref _tintColor, value);
    }

    private float _noiseAmount;

    public float NoiseAmount
    {
        get => _noiseAmount;
        set => SetAndInvalidateEffectGraph(ref _noiseAmount, value);
    }

    protected override void BuildEffectGraph(EffectGraph effectGraph)
    {
        // Create the effect graph effects
        GaussianBlurEffect blurEffect = new();
        TintEffect tintEffect = new();
        PixelShaderEffect<NoiseShader> noiseEffect = new();

        // Connect the effect graph:
        //
        // ┌───────┐   ┌──────┐   ┌──────┐   ┌───────┐   ┌────────┐
        // │ input ├──►│ blur ├──►│ tint ├──►│ noise ├──►│ output │
        // └───────┘   └──────┘   └──────┘   └───────┘   └────────┘
        tintEffect.Source = blurEffect;
        noiseEffect.Sources[0] = tintEffect;

        // Register the effect graph nodes
        effectGraph.RegisterNode(BlurNode, blurEffect);
        effectGraph.RegisterNode(TintNode, tintEffect);
        effectGraph.RegisterOutputNode(NoiseNode, noiseEffect);
    }

    protected override void ConfigureEffectGraph(EffectGraph effectGraph)
    {
        // Update the effect properties
        effectGraph.GetNode(BlurNode).Source = Source;
        effectGraph.GetNode(BlurNode).BlurAmount = BlurAmount;
        effectGraph.GetNode(TintNode).Color = TintColor;
        effectGraph.GetNode(NoiseNode).ConstantBuffer = new NoiseShader(NoiseAmount);
    }
}

@Sergio0694 Sergio0694 added the feature 🎉 A brand new feature for ComputeSharp label Apr 30, 2023
@Sergio0694 Sergio0694 changed the title New CanvasEffect.EffectGraph APIs Add new CanvasEffect.EffectGraph APIs Apr 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 🎉 A brand new feature for ComputeSharp
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant