|
| 1 | +# Future Improvements |
| 2 | + |
| 3 | +Initially when we were having design discussions around Silk.NET 3.0's Windowing API, we wanted to introduce a |
| 4 | +lower-level API upon which our high-level API. The idea being that this would be an extensible API for which there would |
| 5 | +be lower implementation friction and delegating common boilerplate code to a common higher-level implementation i.e. |
| 6 | +there's very little work for us to do in mapping our API into new backends. This would functionally be a PAL, but the |
| 7 | +details of this would depend on the actual requirements we derive as part of designing this API. Ultimately it was |
| 8 | +determined that this work was simply out-of-scope for the initial 3.0 release as the extensibility benefits emerging |
| 9 | +from having a lower-level API was determined to not be a requirement for the initial release, and was not included in |
| 10 | +the original Working Group approved software development plan. |
| 11 | + |
| 12 | +We're well aware this sounds very similar to what our friends at OpenTK are planning for 5.0, for much of the same |
| 13 | +reasons. Indeed we still have community members who are also OpenTK community members that were advocating for it for |
| 14 | +this reason. It's great to consider this sort of prior art, the sharing of insights and lifting eachother up is what |
| 15 | +makes open-source amazing after all. We should also consider how other libraries like SDL and GLFW handle this |
| 16 | +internally. Much like OpenTK, there is motivation for adding a lower-level API to Silk.NET to reduce the implementation |
| 17 | +friction in adding more windowing backends as we believe esoteric platforms like mobile could be served well by them. |
| 18 | +Unlike OpenTK, for desktop there's less appetite due to the shear number of platforms that would cause a lot of |
| 19 | +maintenance effort - OpenTK 4.0 moved to use GLFW because of this, and most of the issues logged before this were |
| 20 | +regarding its per-platform custom implementations, whereas Silk.NET 3.0 is keen to optimise for maintainability and |
| 21 | +delegating maintenance effort to more expert sources (as we have done for SilkTouch by using ClangSharp's P/Invoke |
| 22 | +Generator) like SDL is part of this so we can focus on crafting the best user experience specific to our project. Hence |
| 23 | +why even if we would add this lower-level API, unlike OpenTK 5.0 I don't think we'd use it to implement desktop |
| 24 | +windowing ourselves. But that can change after the initial release, and in any case having this lower-level API would be |
| 25 | +useful. |
| 26 | + |
| 27 | +When reviewing OpenTK specifically, their API design is indeed sound however the mechanisms by which it was exposed to |
| 28 | +the higher-level API left a lot to be desired. Namely, using a dictionary of enums to implementations did not feel like |
| 29 | +the best way to do this. There are likely more intelligent things we can do with the type system to make these patterns |
| 30 | +more JIT friendly and also more extensible - having an enum enumerating the component types requires the extensibility |
| 31 | +model to be defined in a way that is contrary to how the type system works e.g. to define components that are extensions |
| 32 | +beyond our standard set. It was also deemed to be desirable to use `static abstract`s for this sort of low-level API, |
| 33 | +which does help towards JIT friendliness, but this needn't prejudice any future efforts towards these goals - this was |
| 34 | +just an idea. |
| 35 | + |
| 36 | +Ultimately, to make our solution more write-once-run-everywhere, the API design philosophy behind the `Surface` |
| 37 | +type was primarily to make it seem like a modular "component bag" e.g. `window.OpenGL` for OpenGL-specific |
| 38 | +functionality, rather than having specific APIs always exposed as part of the standard interface but only valid for |
| 39 | +usage in specific circumstances. The `IView` separation in 2.X achieved what we wanted somewhat, but this again left a |
| 40 | +lot to be desired given that writing against `IView` instead of `IWindow` is contrary to what most users were doing |
| 41 | +(this is also likely a symptom of being an afterthought introduced quite late into the 1.0 Preview cycle). By using this |
| 42 | +design philosophy, our users have to get used to not assuming that functionality is available, meaning that users are |
| 43 | +encouraged to write in a way that is portable instead of them having to go out of their way by writing against `IView` |
| 44 | +as in 1.X and 2.X. |
| 45 | + |
| 46 | +As for the extensibility goals (i.e. additional components being defined on top of our standard API), my hope was to |
| 47 | +eventually have a `GetComponent` API on `Surface` which things like `window.OpenGL` were defined on top of. This has |
| 48 | +been excluded from the 3.0 initial release, but we could in theory add something like this without the PAL concepts in |
| 49 | +this document being implemented - a component-based architecture for our high-level API and a component-based |
| 50 | +architecture for our low-level API can be developed independently. An example of why we might want this is a virtual |
| 51 | +reality extension that manages the creation of OpenXR bindings from a surface, but this is just one example. It is |
| 52 | +possible that "extension everything" might make this easier on the user while also making it easier for us (e.g. |
| 53 | +extension everything defining an `window.OpenXR` property that implicitly checks the component can be created or |
| 54 | +whatever, `DependentHandle` can probably used for this if we wanted or we could just use `window is IMyComponent` - |
| 55 | +again these are all just ideas, this is just to demonstrate the idea of the API shape). Way earlier in 3.0's development |
| 56 | +we were discussing the use of `IDynamicInterfaceCastable`, but the Silk.NET team were not able to implement support for |
| 57 | +this in the Mono runtime in an acceptable timeframe and complexity level. All of these details depend on how and if we |
| 58 | +make it possible to attach components to existing implementations without requiring modification of the original |
| 59 | +backend. I would quite like this to be the case, but again it depends on the nature of the high-level component-based |
| 60 | +architecture and/or the low-level component-based architecture i.e. where is the extensibility point. |
| 61 | + |
| 62 | +As much as we didn't continue down the path illustrated in this document, it was certainly explored somewhat before we |
| 63 | +decided it wasn't needed for 3.0 (engineers like to overengineer, go figure). The first exploration was essentially a |
| 64 | +static dependency injection API i.e. a [`IHluComponentRegistry`](https://github.com/dotnet/Silk.NET/blob/56af8e1b34dc41a43de10dff45d09d25f12e8e57/sources/Core/Core/Abstractions/IHluComponentRegistry.cs) |
| 65 | +provides components (these can be changed together for extensibility) that configures a [`Surface`](https://github.com/dotnet/Silk.NET/blob/56af8e1b34dc41a43de10dff45d09d25f12e8e57/sources/Windowing/Common/Surface.cs) |
| 66 | +(well, a [`IHluComponentHost`](https://github.com/dotnet/Silk.NET/blob/56af8e1b34dc41a43de10dff45d09d25f12e8e57/sources/Core/Core/Abstractions/IHluComponentHost.cs) |
| 67 | +which `Surface` implements) with the components. There was also some [source generator magic](https://github.com/dotnet/Silk.NET/blob/56af8e1b34dc41a43de10dff45d09d25f12e8e57/sources/Core/Analyzers/HluSourceGenerator.Hosts.cs) |
| 68 | +explored to make this more JIT friendly, but that itself had some downsides e.g. one object being an implementation type |
| 69 | +of multiple component types had two references stored in the surface. These problems aren't insurmountable but |
| 70 | +ultimately it was determined that making an entire dependency injection API just for this was a bit silly. |
| 71 | + |
| 72 | +After this attempt at implementing these concepts, another attempt was made that encompassed the low-level API desired |
| 73 | +to reduce implementation friction. Essentially, [`ISurfaceHost`](https://github.com/dotnet/Silk.NET/blob/129d4957ce1058252723add2f6890fb53f234432/sources/Windowing/Common/Hosting/ISurfaceHost.cs) |
| 74 | +had a bunch of lower-level APIs as `static abstract`s that essentially boiled down to "get a property, set a property" |
| 75 | +on surface objects or surface requests. Didn't quite get round to implementing the "additional component" extensibility |
| 76 | +concepts described but this could likely be done using type chaining and essentially changing those get/set property |
| 77 | +methods to accept a generic "property type", but again these are just ideas - this was never realised or prototyped. |
| 78 | +This was progressing well enough, and had some decent benefits as well like centralising all the [multi-threading logic](https://github.com/dotnet/Silk.NET/blob/129d4957ce1058252723add2f6890fb53f234432/sources/Windowing/Common/Hosting/MultiThreadedSurfaceHost%601.cs) |
| 79 | +at the lowest level of implementation. |
| 80 | + |
| 81 | +All in all, there's a lot of benefits to having a modular, component-based, and extensible approach to designing our |
| 82 | +windowing API and this is definitely something we're keen to pursue. But for now, we determined that for the 3.0 initial |
| 83 | +release we only needed to do this for the user-facing API (as per the goals stated in the SDP to make the API more |
| 84 | +encouraging of write-once-run-everywhere) and as much as we want to fulfill that `GetComponent` extensibility vision to |
| 85 | +allow extensions of our standard API set, that also isn't needed for the initial release. Nonetheless, it was key to |
| 86 | +ensure we had enough jumping off points to ensure this can be implemented in the future, and also to implement the |
| 87 | +lower-level, implementation-facing API to make our life easier if we did want to add more backends outside of SDL. |
0 commit comments