@@ -4,29 +4,29 @@ Originally posted on Ryan's Google+ account.
4
4
5
5
Background:
6
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
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
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
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
15
dynamic loader to ignore their SDL2 in this case, of course.
16
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
17
+ Steam Runtime, or just tried to run the game from the command line instead of
18
18
Steam might find themselves unable to run the game, due to a missing dependency.
19
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
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
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
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
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.
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
30
Static linking stops this dead.
31
31
32
32
So here's what we did:
@@ -45,7 +45,7 @@ Except that is all done with a bunch of macro magic so we don't have to maintain
45
45
every one of these.
46
46
47
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
48
+ SDL_Init() that you've been calling all this time. But at startup, it looks more
49
49
like this:
50
50
51
51
```c
@@ -56,83 +56,83 @@ Uint32 SDL_Init_DEFAULT(Uint32 flags)
56
56
}
57
57
```
58
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.
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
61
First call to any SDL function sets the whole thing up.
62
62
63
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
64
+ system's dynamic loader was supposed to do for us? Yes, but now we've got this
65
65
level of indirection, we can do things like this:
66
66
67
67
``` bash
68
68
export SDL_DYNAMIC_API=/my/actual/libSDL-2.0.so.0
69
69
./MyGameThatIsStaticallyLinkedToSDL2
70
70
```
71
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
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
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,
75
+ is desired, the statically linked version will provide its own jump table,
76
76
and everyone is happy.
77
77
78
78
So now:
79
- - Developers can statically link SDL, and users can still replace it.
79
+ - Developers can statically link SDL, and users can still replace it.
80
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,
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
83
but it'll also just work in the default case.
84
- - Developers can ship the same package to everyone (Humble Bundle, GOG, etc),
84
+ - Developers can ship the same package to everyone (Humble Bundle, GOG, etc),
85
85
and it'll do the right thing.
86
- - End users (and Valve) can update a game's SDL in almost any case,
86
+ - End users (and Valve) can update a game's SDL in almost any case,
87
87
to keep abandoned games running on newer platforms.
88
- - Everyone develops with SDL exactly as they have been doing all along.
88
+ - Everyone develops with SDL exactly as they have been doing all along.
89
89
Same headers, same ABI. Just get the latest version to enable this magic.
90
90
91
91
92
92
A little more about SDL_InitDynamicAPI():
93
93
94
- Internally, InitAPI does some locking to make sure everything waits until a
95
- single thread initializes everything (although even SDL_CreateThread() goes
94
+ Internally, InitAPI does some locking to make sure everything waits until a
95
+ single thread initializes everything (although even SDL_CreateThread() goes
96
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
97
+ an external SDL library. If not, it sets up the jump table using the current
98
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
99
+ a shared library of its own). If so, it loads that library and looks for and
100
100
calls a single function:
101
101
102
102
``` c
103
103
SInt32 SDL_DYNAPI_entry (Uint32 version, void * table, Uint32 tablesize);
104
104
```
105
105
106
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
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
110
the needed functions if tablesize <= sizeof its own jump table. If tablesize is
111
111
bigger (say, SDL 2.0.4 is trying to load SDL 2.0.3), then we know to abort, but
112
112
if it's smaller, we know we can provide the entire API that the caller needs.
113
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
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
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
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
122
Steam Client, this isn't a bad option.
123
123
124
124
Finally, I'm sure some people are reading this and thinking,
125
- "I don't want that overhead in my project!"
125
+ "I don't want that overhead in my project!"
126
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
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
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
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
138
nudged everyone towards what we think is the best solution.
0 commit comments