Skip to content

Commit bd91c25

Browse files
committed
Add a write-up on the PAL HLU concepts/status for the future
1 parent 5224e0b commit bd91c25

File tree

14 files changed

+93
-269
lines changed

14 files changed

+93
-269
lines changed

.gitignore

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,6 @@ src/**/build/
474474
*.gen.sln
475475
*.gen.txt
476476

477-
# Silk.NET Website Generation Output
478-
/docs/
479-
src/Website/Silk.NET.Statiq/temp
480-
src/Website/Silk.NET.Statiq/cache
481-
482477
# SilkTouch configs
483478
!eng/silktouch/**/*.rsp
484479

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

docs/for-contributors/generators/README.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

docs/for-contributors/generators/emitter/README.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/for-contributors/generators/emitter/about formatting.md

Lines changed: 0 additions & 38 deletions
This file was deleted.

docs/for-contributors/generators/emitter/visitor.md

Lines changed: 0 additions & 39 deletions
This file was deleted.

docs/for-contributors/generators/scraper.md

Lines changed: 0 additions & 16 deletions
This file was deleted.

docs/for-contributors/generators/symbol-layer/README.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

docs/for-contributors/generators/symbol-layer/symbol-visitor.md

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)