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