diff options
author | 3gg <3gg@shellblade.net> | 2024-05-04 16:51:29 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2024-05-04 16:51:29 -0700 |
commit | 8222bfe56d4dabe8d92fc4b25ea1b0163b16f3e1 (patch) | |
tree | 763389e42276035ac134d94eb1dc32293b88d807 /src/contrib/SDL-2.30.2/docs/README-dynapi.md |
Initial commit.
Diffstat (limited to 'src/contrib/SDL-2.30.2/docs/README-dynapi.md')
-rw-r--r-- | src/contrib/SDL-2.30.2/docs/README-dynapi.md | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/contrib/SDL-2.30.2/docs/README-dynapi.md b/src/contrib/SDL-2.30.2/docs/README-dynapi.md new file mode 100644 index 0000000..76b868c --- /dev/null +++ b/src/contrib/SDL-2.30.2/docs/README-dynapi.md | |||
@@ -0,0 +1,138 @@ | |||
1 | # Dynamic API | ||
2 | |||
3 | Originally posted on Ryan's Google+ account. | ||
4 | |||
5 | Background: | ||
6 | |||
7 | - The Steam Runtime has (at least in theory) a really kick-ass build of SDL2, | ||
8 | but developers are shipping their own SDL2 with individual Steam games. | ||
9 | These games might stop getting updates, but a newer SDL2 might be needed later. | ||
10 | Certainly we'll always be fixing bugs in SDL, even if a new video target isn't | ||
11 | ever needed, and these fixes won't make it to a game shipping its own SDL. | ||
12 | - Even if we replace the SDL2 in those games with a compatible one, that is to | ||
13 | say, edit a developer's Steam depot (yuck!), there are developers that are | ||
14 | statically linking SDL2 that we can't do this for. We can't even force the | ||
15 | dynamic loader to ignore their SDL2 in this case, of course. | ||
16 | - If you don't ship an SDL2 with the game in some form, people that disabled the | ||
17 | Steam Runtime, or just tried to run the game from the command line instead of | ||
18 | Steam might find themselves unable to run the game, due to a missing dependency. | ||
19 | - If you want to ship on non-Steam platforms like GOG or Humble Bundle, or target | ||
20 | generic Linux boxes that may or may not have SDL2 installed, you have to ship | ||
21 | the library or risk a total failure to launch. So now, you might have to have | ||
22 | a non-Steam build plus a Steam build (that is, one with and one without SDL2 | ||
23 | included), which is inconvenient if you could have had one universal build | ||
24 | that works everywhere. | ||
25 | - We like the zlib license, but the biggest complaint from the open source | ||
26 | community about the license change is the static linking. The LGPL forced this | ||
27 | as a legal, not technical issue, but zlib doesn't care. Even those that aren't | ||
28 | concerned about the GNU freedoms found themselves solving the same problems: | ||
29 | swapping in a newer SDL to an older game often times can save the day. | ||
30 | Static linking stops this dead. | ||
31 | |||
32 | So here's what we did: | ||
33 | |||
34 | SDL now has, internally, a table of function pointers. So, this is what SDL_Init | ||
35 | now looks like: | ||
36 | |||
37 | ```c | ||
38 | Uint32 SDL_Init(Uint32 flags) | ||
39 | { | ||
40 | return jump_table.SDL_Init(flags); | ||
41 | } | ||
42 | ``` | ||
43 | |||
44 | Except that is all done with a bunch of macro magic so we don't have to maintain | ||
45 | every one of these. | ||
46 | |||
47 | What is jump_table.SDL_init()? Eventually, that's a function pointer of the real | ||
48 | SDL_Init() that you've been calling all this time. But at startup, it looks more | ||
49 | like this: | ||
50 | |||
51 | ```c | ||
52 | Uint32 SDL_Init_DEFAULT(Uint32 flags) | ||
53 | { | ||
54 | SDL_InitDynamicAPI(); | ||
55 | return jump_table.SDL_Init(flags); | ||
56 | } | ||
57 | ``` | ||
58 | |||
59 | SDL_InitDynamicAPI() fills in jump_table with all the actual SDL function | ||
60 | pointers, which means that this `_DEFAULT` function never gets called again. | ||
61 | First call to any SDL function sets the whole thing up. | ||
62 | |||
63 | So you might be asking, what was the value in that? Isn't this what the operating | ||
64 | system's dynamic loader was supposed to do for us? Yes, but now we've got this | ||
65 | level of indirection, we can do things like this: | ||
66 | |||
67 | ```bash | ||
68 | export SDL_DYNAMIC_API=/my/actual/libSDL-2.0.so.0 | ||
69 | ./MyGameThatIsStaticallyLinkedToSDL2 | ||
70 | ``` | ||
71 | |||
72 | And now, this game that is statically linked to SDL, can still be overridden | ||
73 | with a newer, or better, SDL. The statically linked one will only be used as | ||
74 | far as calling into the jump table in this case. But in cases where no override | ||
75 | is desired, the statically linked version will provide its own jump table, | ||
76 | and everyone is happy. | ||
77 | |||
78 | So now: | ||
79 | - Developers can statically link SDL, and users can still replace it. | ||
80 | (We'd still rather you ship a shared library, though!) | ||
81 | - Developers can ship an SDL with their game, Valve can override it for, say, | ||
82 | new features on SteamOS, or distros can override it for their own needs, | ||
83 | but it'll also just work in the default case. | ||
84 | - Developers can ship the same package to everyone (Humble Bundle, GOG, etc), | ||
85 | and it'll do the right thing. | ||
86 | - End users (and Valve) can update a game's SDL in almost any case, | ||
87 | to keep abandoned games running on newer platforms. | ||
88 | - Everyone develops with SDL exactly as they have been doing all along. | ||
89 | Same headers, same ABI. Just get the latest version to enable this magic. | ||
90 | |||
91 | |||
92 | A little more about SDL_InitDynamicAPI(): | ||
93 | |||
94 | Internally, InitAPI does some locking to make sure everything waits until a | ||
95 | single thread initializes everything (although even SDL_CreateThread() goes | ||
96 | through here before spinning a thread, too), and then decides if it should use | ||
97 | an external SDL library. If not, it sets up the jump table using the current | ||
98 | SDL's function pointers (which might be statically linked into a program, or in | ||
99 | a shared library of its own). If so, it loads that library and looks for and | ||
100 | calls a single function: | ||
101 | |||
102 | ```c | ||
103 | Sint32 SDL_DYNAPI_entry(Uint32 version, void *table, Uint32 tablesize); | ||
104 | ``` | ||
105 | |||
106 | That function takes a version number (more on that in a moment), the address of | ||
107 | the jump table, and the size, in bytes, of the table. | ||
108 | Now, we've got policy here: this table's layout never changes; new stuff gets | ||
109 | added to the end. Therefore SDL_DYNAPI_entry() knows that it can provide all | ||
110 | the needed functions if tablesize <= sizeof its own jump table. If tablesize is | ||
111 | bigger (say, SDL 2.0.4 is trying to load SDL 2.0.3), then we know to abort, but | ||
112 | if it's smaller, we know we can provide the entire API that the caller needs. | ||
113 | |||
114 | The version variable is a failsafe switch. | ||
115 | Right now it's always 1. This number changes when there are major API changes | ||
116 | (so we know if the tablesize might be smaller, or entries in it have changed). | ||
117 | Right now SDL_DYNAPI_entry gives up if the version doesn't match, but it's not | ||
118 | inconceivable to have a small dispatch library that only supplies this one | ||
119 | function and loads different, otherwise-incompatible SDL libraries and has the | ||
120 | right one initialize the jump table based on the version. For something that | ||
121 | must generically catch lots of different versions of SDL over time, like the | ||
122 | Steam Client, this isn't a bad option. | ||
123 | |||
124 | Finally, I'm sure some people are reading this and thinking, | ||
125 | "I don't want that overhead in my project!" | ||
126 | |||
127 | To which I would point out that the extra function call through the jump table | ||
128 | probably wouldn't even show up in a profile, but lucky you: this can all be | ||
129 | disabled. You can build SDL without this if you absolutely must, but we would | ||
130 | encourage you not to do that. However, on heavily locked down platforms like | ||
131 | iOS, or maybe when debugging, it makes sense to disable it. The way this is | ||
132 | designed in SDL, you just have to change one #define, and the entire system | ||
133 | vaporizes out, and SDL functions exactly like it always did. Most of it is | ||
134 | macro magic, so the system is contained to one C file and a few headers. | ||
135 | However, this is on by default and you have to edit a header file to turn it | ||
136 | off. Our hopes is that if we make it easy to disable, but not too easy, | ||
137 | everyone will ultimately be able to get what they want, but we've gently | ||
138 | nudged everyone towards what we think is the best solution. | ||