diff --git a/documentation/proposals/Proposal - Chain Polymorphism.md b/documentation/proposals/Proposal - Chain Polymorphism.md new file mode 100644 index 0000000000..276e72696c --- /dev/null +++ b/documentation/proposals/Proposal - Chain Polymorphism.md @@ -0,0 +1,106 @@ +# Summary + +Using the managed `Silk.NET.Vulkan.Chain` and its generically-typed descendants can be unwieldy in the specific case of recieving chains +with known starting element(s) but potential unknown later elements, e.g. a `SwapchainCreateInfoKHR` which may or may not have an +`ImageCreateInfo` later in the chain. If use of the managed chain api is desired in this case, the untyped `Chain` type must be used +and the result of its indexer cast to the desired chain start type. This, combined with the public api surface on ``Chain`2`` being +a strict superset of ``Chain`1`` etc., a polymorphic interface is desired for handling variable-size chains. + +# Contributors + +- [Khitiara](https://github.com/Khitiara) + +# Current Status + +- [x] Proposed +- [x] Discussed with API Review Board (ARB) +- [x] Approved +- [ ] Implemented + +# Design Decisions + +- This is mainly useful in cases where the managed api and its automatic management of chain memory is desired. The unmanaged non-generic API + already supports this use-case, so this function is mainly beneficial as method return types where chain extensions may not be known ahead of + time, and thus `out` parameters for all unmanaged chain elements are not suitable. + +# Proposed API + +A series of generic interfaces are created to accompany the generic `Chain<...>` classes: + +```csharp +public unsafe interface IChain : IDisposable + where TChain : unmanaged, IChainable { + public BaseInStructure* HeadPtr { get; } + public TChain Head { get; set; } +} + +public unsafe interface IChain : IChain + where TChain : unmanaged, IChainable + where T1 : unmanaged, IChainable { + public BaseInStructure* Item1Ptr { get; } + public T1 Item1 { get; set; } +} + +public unsafe interface IChain : IChain + where TChain : unmanaged, IChainable + where T1 : unmanaged, IChainable + where T2 : unmanaged, IChainable { + public BaseInStructure* Item2Ptr { get; } + public T2 Item2 { get; set; } +} + +... +``` + +and the existing generic `Chain<...>` types are modified to implement these new interfaces: + +```csharp +public unsafe sealed class Chain : Chain, IEquatable>, IChain + where TChain : unmanaged, IChainable { + ... +} + +public unsafe sealed class Chain : Chain, IEquatable>, IChain + where TChain : unmanaged, IChainable + where T1 : unmanaged, IChainable { + ... +} + +public unsafe sealed class Chain : Chain, IEquatable>, IChain + where TChain : unmanaged, IChainable + where T1 : unmanaged, IChainable + where T2 : unmanaged, IChainable { + ... +} + +... +``` + +Usage: + +```csharp +public IChain DeduceImageCreateInfo(IChain swapchainCreateInfo) { + ImageCreateInfo createInfo = ...; + + // condition here used for example - a TryFind extension or similar may be desirable in the long term + if (swapchainCreateInfo is Chain(_, formatListCreateInfo)) { + // Chain : IChain : IChain + return Chain.Create(createInfo, formatListCreateInfo); + } + + // Chain : IChain + return Chain.Create(createInfo); +} + +... + +public void Foo() { + SwapchainCreateInfoKHR sci = ...; + using IChain ici = DeduceImageCreateInfo(sci); + ReadOnlySpan fmts = ici switch { + Chain(_, var formatList) => + new ReadOnlySpan(formatList.PViewFormats, (int)formatList.ViewFormatCount).ToArray(), + Chain(var i) => stackalloc[] { i.Format } + } +} +``` diff --git a/src/Vulkan/Silk.NET.Vulkan/Chain.cs b/src/Vulkan/Silk.NET.Vulkan/Chain.cs index 321bad70a3..81af39c67c 100644 --- a/src/Vulkan/Silk.NET.Vulkan/Chain.cs +++ b/src/Vulkan/Silk.NET.Vulkan/Chain.cs @@ -14,7 +14,7 @@ namespace Silk.NET.Vulkan; /// Base class for all Managed Chains. /// public abstract unsafe partial class Chain : IReadOnlyList, IEquatable, IDisposable -{ +{ /// /// Gets a pointer to the current head. ///