diff --git a/src/ComputeSharp.D2D1.UI/CanvasEffect.EffectGraph.cs b/src/ComputeSharp.D2D1.UI/CanvasEffect.EffectGraph.cs
deleted file mode 100644
index 9af0f2a7a..000000000
--- a/src/ComputeSharp.D2D1.UI/CanvasEffect.EffectGraph.cs
+++ /dev/null
@@ -1,265 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using Microsoft.Graphics.Canvas;
-
-#if WINDOWS_UWP
-namespace ComputeSharp.D2D1.Uwp;
-#else
-namespace ComputeSharp.D2D1.WinUI;
-#endif
-
-///
-partial class CanvasEffect
-{
- ///
- /// An object representing an effect graph being built or configured.
- ///
- protected readonly ref struct EffectGraph
- {
- ///
- ///The owning instance.
- ///
- private readonly CanvasEffect owner;
-
- ///
- /// Creates a new instance with the specified parameters.
- ///
- /// The owning instance.
- internal EffectGraph(CanvasEffect owner)
- {
- this.owner = owner;
- }
-
- ///
- /// Gets a previously registered object associated with a given effect graph node.
- ///
- /// The instance to use to lookup the object to retrieve.
- /// The object associated with in the effect graph.
- /// Thrown if is .
- /// Thrown if is not currently registered in the effect graph.
- /// Thrown if the current instance is not valid.
- public ICanvasImage GetNode(IEffectNode effectNode)
- {
- default(ArgumentNullException).ThrowIfNull(effectNode);
- default(InvalidOperationException).ThrowIf(this.owner is null);
-
- // Try to get the canvas image associated with the input effect node marker.
- // This must have been previously registered in a call to BuildEffectGraph.
- if (!this.owner.transformNodes.TryGetValue(effectNode, out ICanvasImage? canvasImage))
- {
- default(ArgumentException).Throw(nameof(effectNode), "The specified node is not registered in the effect graph.");
- }
-
- return canvasImage;
- }
-
- ///
- /// Gets a previously registered object (an instance) associated with a given effect graph node.
- ///
- /// The type of object to retrieve.
- /// The instance to use to lookup the object to retrieve.
- /// The object associated with in the effect graph.
- /// Thrown if is .
- /// Thrown if is not currently registered in the effect graph.
- /// Thrown if the current instance is not valid.
- public T GetNode(IEffectNode effectNode)
- where T : class, ICanvasImage
- {
- default(ArgumentNullException).ThrowIfNull(effectNode);
- default(InvalidOperationException).ThrowIf(this.owner is null);
-
- // Retrieve the registered canvas image, same as above
- if (!this.owner.transformNodes.TryGetValue(effectNode, out ICanvasImage? canvasImage))
- {
- default(ArgumentException).Throw(nameof(effectNode), "The specified node is not registered in the effect graph.");
- }
-
- // Return the T node (we can skip the expensive cast since this is guaranteed to be valid).
- // This is because EffectNode being registered can only associate T instances with them.
- return Unsafe.As(canvasImage);
- }
-
- ///
- /// Registers an object in the effect graph.
- ///
- /// The object to register in the effect graph.
- /// Thrown if is .
- ///
- /// Thrown if the current instance is not valid or if the effect graph does not support modifications at this time.
- ///
- ///
- ///
- ///
- /// This method can be used when doesn't need to be retrieved from , ie.
- /// when it has no properties that the current effect instance will need to mutate over it. Using this method still allows the effect to
- /// correctly track the image ownership, so that it can be disposed when the current effect instance is disposed.
- ///
- ///
- /// If performing lookups on is required, use the overload.
- ///
- ///
- public void RegisterNode(ICanvasImage canvasImage)
- {
- default(ArgumentNullException).ThrowIfNull(canvasImage);
- default(InvalidOperationException).ThrowIf(this.owner is null);
- default(InvalidOperationException).ThrowIf(this.owner.invalidationType != InvalidationType.Creation, "The effect graph cannot be modified after its creation.");
-
- // Use a new dummy object as key to register the anonymous effect node. This is cheaper than having to maintain a different
- // data structure (eg. a HashSet instance) just to hold the anonymous effects that may be registered. This way,
- // the same dictionary can be reused for all kinds of effect nodes instead. Anonymous effects are generally a nicher scenario.
- _ = this.owner.transformNodes.TryAdd(new object(), canvasImage);
- }
-
- ///
- /// Registers an object in the effect graph, associated with a given instance.
- ///
- /// The type of object to register.
- /// The instance to use to register .
- /// The object to register in the effect graph.
- /// Thrown if or are .
- ///
- /// Thrown if the current instance is not valid or if the effect graph does not support modifications at this time.
- ///
- /// Thrown if is already registered in the effect graph.
- ///
- /// This method can only be called from an value being passed to . If this
- /// method is called from within , it will fail, as the effect graph cannot be mutated from there.
- ///
- public void RegisterNode(EffectNode effectNode, T canvasImage)
- where T : class, ICanvasImage
- {
- default(ArgumentNullException).ThrowIfNull(effectNode);
- default(ArgumentNullException).ThrowIfNull(canvasImage);
- default(InvalidOperationException).ThrowIf(this.owner is null);
- default(InvalidOperationException).ThrowIf(this.owner.invalidationType != InvalidationType.Creation, "The effect graph cannot be modified after its creation.");
-
- // Try to add the new canvas image associated with the input effect node marker.
- // This must not have been previously added from another RegisterNode call.
- if (!this.owner.transformNodes.TryAdd(effectNode, canvasImage))
- {
- default(ArgumentException).Throw(nameof(effectNode), "The specified node is already registered in the effect graph.");
- }
- }
-
- ///
- /// Registers an object in the effect graph, and marks it as the output node for the effect graph being built.
- ///
- ///
- ///
- ///
- /// This method can be used when doesn't need to be retrieved from , ie.
- /// when it has no properties that the current effect instance will need to mutate over it. Using this method still allows the effect to
- /// correctly track the image ownership, so that it can be disposed when the current effect instance is disposed.
- ///
- ///
- /// If performing lookups on is required, use the overload.
- ///
- ///
- ///
- public void RegisterOutputNode(ICanvasImage canvasImage)
- {
- default(ArgumentNullException).ThrowIfNull(canvasImage);
- default(InvalidOperationException).ThrowIf(this.owner is null);
- default(InvalidOperationException).ThrowIf(this.owner.invalidationType != InvalidationType.Creation, "The effect graph cannot be modified after its creation.");
-
- // Register the anonymous effect node with a dummy object, same as above
- _ = this.owner.transformNodes.TryAdd(new object(), canvasImage);
-
- // Store the anonymous output node for later use
- this.owner.canvasImage = canvasImage;
- }
-
- ///
- /// Registers an object in the effect graph, associated with a given instance.
- /// Additionally, it also marks the input object as the output node for the effect graph being built.
- ///
- ///
- public void RegisterOutputNode(EffectNode effectNode, T canvasImage)
- where T : class, ICanvasImage
- {
- default(ArgumentNullException).ThrowIfNull(effectNode);
- default(ArgumentNullException).ThrowIfNull(canvasImage);
- default(InvalidOperationException).ThrowIf(this.owner is null);
- default(InvalidOperationException).ThrowIf(this.owner.invalidationType != InvalidationType.Creation, "The effect graph cannot be modified after its creation.");
-
- // Try to add the output node as in the method above (but with an extra check before doing so)
- if (!this.owner.transformNodes.TryAdd(effectNode, canvasImage))
- {
- default(ArgumentException).Throw(nameof(effectNode), "The specified node is already registered in the effect graph.");
- }
-
- // Store the output node for later use
- this.owner.canvasImage = canvasImage;
- }
-
- ///
- /// Sets a previously registered instance as the output node for the effect graph.
- ///
- /// The instance to set as output node for the effect graph.
- /// Thrown if is .
- /// Thrown if the current instance is not valid.
- /// Thrown if is not registered in the effect graph.
- ///
- /// This method can be called from both and . It can be used for
- /// efficiently changing the output node of a graph, without having to rebuild it. For instance, this can be used for effects that
- /// have multiple paths that can be taken for their inputs, depending on the value of some property describing the effect behavior.
- ///
- public void SetOutputNode(IEffectNode effectNode)
- {
- default(ArgumentNullException).ThrowIfNull(effectNode);
- default(InvalidOperationException).ThrowIf(this.owner is null);
-
- // Get the canvas image for the registered effect node (which has to exist at this point)
- if (!this.owner.transformNodes.TryGetValue(effectNode, out ICanvasImage? canvasImage))
- {
- default(ArgumentException).Throw(nameof(effectNode), "The specified output node is not registered in the effect graph.");
- }
-
- // Store the new output node
- this.owner.canvasImage = canvasImage;
- }
- }
-
- ///
- /// A marker interface for an effect node that was registered from an value.
- ///
- ///
- ///
- /// This interface allows using and in more scenarios,
- /// including where the concrete type of the image being used (eg. to set the output node) is not known (or needed) by the caller.
- ///
- ///
- /// This interface only implemented by and it's not meant to be implemented by external types.
- ///
- ///
- protected interface IEffectNode
- {
- }
-
- ///
- /// A marker interface for a generic effect node that was registered from an value.
- ///
- /// The covariant type of associated with the current effect node.
- ///
- ///
- /// This interface allows using and in more scenarios,
- /// including with ternary expressions returning multiple concrete instances with a common image type.
- ///
- ///
- /// This interface only implemented by and it's not meant to be implemented by external types.
- ///
- ///
- protected interface IEffectNode : IEffectNode
- where T : ICanvasImage
- {
- }
-
- ///
- /// A marker type for an effect node that can be registered and retrieved from an value.
- ///
- /// The type of associated with the current effect node.
- protected sealed class EffectNode : IEffectNode
- where T : class, ICanvasImage
- {
- }
-}
\ No newline at end of file
diff --git a/src/ComputeSharp.D2D1.UI/CanvasEffect.Interop.cs b/src/ComputeSharp.D2D1.UI/CanvasEffect.Interop.cs
index 3f46123ef..4fc17ae56 100644
--- a/src/ComputeSharp.D2D1.UI/CanvasEffect.Interop.cs
+++ b/src/ComputeSharp.D2D1.UI/CanvasEffect.Interop.cs
@@ -66,40 +66,40 @@ unsafe int ICanvasImageInterop.Interface.GetD2DImage(
///
/// The current instance.
/// Thrown if the current instance is disposed.
- [MemberNotNull(nameof(canvasImage))]
+ [MemberNotNull(nameof(CanvasImage))]
private ICanvasImage GetCanvasImage()
{
- lock (this.transformNodes)
+ lock (this.TransformNodes)
{
default(ObjectDisposedException).ThrowIf(this.isDisposed, this);
// Build the effect graph (the output node might not have been set yet)
- if (this.invalidationType == InvalidationType.Creation)
+ if (this.InvalidationType == CanvasEffectInvalidationType.Creation)
{
DisposeEffectGraph();
- BuildEffectGraph(new EffectGraph(this));
+ BuildEffectGraph(new CanvasEffectGraph(this));
// We successfully got past the effect graph creation, so update the current
// invalidation state. This ensures that even if the configuration failed, the
// next time the effect is drawn again the graph won't be created again too.
- this.invalidationType = InvalidationType.Update;
+ this.InvalidationType = CanvasEffectInvalidationType.Update;
}
// Configure the effect graph, if the effect is invalidated
- if (this.invalidationType == InvalidationType.Update)
+ if (this.InvalidationType == CanvasEffectInvalidationType.Update)
{
- ConfigureEffectGraph(new EffectGraph(this));
+ ConfigureEffectGraph(new CanvasEffectGraph(this));
// The effect graph is now ready to go: no further work will be done before drawing
// unless it is explicitly invalidated again, using either creation or update mode.
- this.invalidationType = default;
+ this.InvalidationType = default;
}
// At this point, there must be an output canvas image being set.
// If not, it means a derived type has forgot to set an output node.
- default(InvalidOperationException).ThrowIf(this.canvasImage is null, "No output node is set in the effect graph.");
+ default(InvalidOperationException).ThrowIf(this.CanvasImage is null, "No output node is set in the effect graph.");
- return this.canvasImage;
+ return this.CanvasImage;
}
}
@@ -109,7 +109,7 @@ private ICanvasImage GetCanvasImage()
private void DisposeEffectGraph()
{
// Dispose all registered canvas images
- foreach (ICanvasImage canvasImage in this.transformNodes.Values)
+ foreach (ICanvasImage canvasImage in this.TransformNodes.Values)
{
canvasImage.Dispose();
}
@@ -117,7 +117,7 @@ private void DisposeEffectGraph()
// Also clear the current effect graph. Note that the canvas image used as output
// node for the effect graph does not need to be explicitly disposed here, as that
// object is guaranteed to have been part of the dictionary of transform nodes.
- this.transformNodes.Clear();
- this.canvasImage = null;
+ this.TransformNodes.Clear();
+ this.CanvasImage = null;
}
}
\ No newline at end of file
diff --git a/src/ComputeSharp.D2D1.UI/CanvasEffect.cs b/src/ComputeSharp.D2D1.UI/CanvasEffect.cs
index c9cdbf630..a63f3f3b3 100644
--- a/src/ComputeSharp.D2D1.UI/CanvasEffect.cs
+++ b/src/ComputeSharp.D2D1.UI/CanvasEffect.cs
@@ -19,19 +19,25 @@ public abstract partial class CanvasEffect : ICanvasImage, ICanvasImageInterop.I
/// The mapping of registered transform nodes for the current effect graph.
///
///
- /// When not empty (ie. when an effect graph has been built), this will always also include .
+ ///
+ /// When not empty (ie. when an effect graph has been built), this will always also include .
+ ///
+ ///
+ /// This field and the two below are due to lack of modifier in C#.
+ /// The type is the only one that needs access to these fields to configure the effect.
+ ///
///
- private readonly Dictionary