Browse code

WIP: Add GLBackendSDL

Robert Cranston authored on 12/10/2021 04:12:23
Showing 9 changed files

1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
1
+# SDL
2
+# leak:libX11
... ...
@@ -24,8 +24,12 @@ if(glfw3_FOUND)
24 24
     list(APPEND GLBACKEND_DEFINITIONS  GLBACKEND_GLFW)
25 25
 endif()
26 26
 
27
-## Cache
28
-set(GLBACKEND_DEFINITIONS "${GLBACKEND_DEFINITIONS}" CACHE INTERNAL "")
27
+## sdl
28
+find_package(SDL2)
29
+if(SDL2_FOUND)
30
+    list(APPEND GLBACKEND_DEPENDENCIES SDL2)
31
+    list(APPEND GLBACKEND_DEFINITIONS  GLBACKEND_SDL)
32
+endif()
29 33
 
30 34
 ## Common
31 35
 include(common.cmake)
... ...
@@ -7,6 +7,7 @@ Currently supported backends:
7 7
 | Name     | Category        | Define           | Include              | Class         |
8 8
 | --       | --              | --               | --                   | --            |
9 9
 | [GLFW][] | Windowing/input | `GLBACKEND_GLFW` | `glbackend_glfw.hpp` | `BackendGLFW` |
10
+| [SDL2][] | Multimedia      | `GLBACKEND_SDL`  | `glbackend_sdl.hpp`  | `BackendSDL`  |
10 11
 
11 12
 [`glbackend`]: https://git.rcrnstn.net/rcrnstn/glbackend
12 13
 [C++11]: https://en.wikipedia.org/wiki/C++11
... ...
@@ -14,6 +15,7 @@ Currently supported backends:
14 15
 [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
15 16
 [backend]: https://www.khronos.org/opengl/wiki/Related_toolkits_and_APIs#OpenGL_initialization
16 17
 [GLFW]: https://www.glfw.org
18
+[SDL2]: https://www.libsdl.org
17 19
 
18 20
 ## Usage
19 21
 
... ...
@@ -334,11 +336,13 @@ System dependencies that need to be installed:
334 336
     -   [OpenGL Extension Wrangler (GLEW)][] (e.g. [`libglew-dev`][]).
335 337
 -   Private (tests):
336 338
     -   [GLFW][] (e.g. [`libglfw3-dev`][]).
339
+    -   [SDL2][] (e.g. [`libsdl2-dev`][]).
337 340
 
338 341
 [OpenGL Extension Wrangler (GLEW)]: http://glew.sourceforge.net
339 342
 [`libgl1-mesa-dev`]: https://packages.debian.org/search?keywords=libgl1-mesa-dev
340 343
 [`libglew-dev`]: https://packages.debian.org/search?keywords=libglew-dev
341 344
 [`libglfw3-dev`]: https://packages.debian.org/search?keywords=libglfw3-dev
345
+[`libsdl2-dev`]: https://packages.debian.org/search?keywords=libsdl2-dev
342 346
 
343 347
 ## Build system
344 348
 
345 349
new file mode 100644
346 350
Binary files /dev/null and b/assets/tests/GLBackendSDL.tga differ
... ...
@@ -5,6 +5,9 @@
5 5
 #if defined(GLBACKEND_GLFW)
6 6
     #include "glbackend_glfw.hpp" // IWYU pragma: export
7 7
     using GLBackendDefault = GLBackendGLFW;
8
+#elif defined(GLBACKEND_SDL)
9
+    #include "glbackend_sdl.hpp" // IWYU pragma: export
10
+    using GLBackendDefault = GLBackendSDL;
8 11
 #else
9 12
     #error "No GLBackendDefault configured"
10 13
 #endif
11 14
new file mode 100644
... ...
@@ -0,0 +1,166 @@
1
+/// Guards
2
+
3
+#ifdef  GLBACKEND_SDL
4
+#ifndef GLBACKEND_SDL_HPP_
5
+#define GLBACKEND_SDL_HPP_
6
+
7
+
8
+/// Includes
9
+
10
+#include <array>
11
+#include <string>
12
+
13
+#include <glbackend.hpp>
14
+
15
+// #include <SDL2/SDL.h>
16
+#include <SDL2/SDL_keyboard.h>
17
+#include <SDL2/SDL_mouse.h>
18
+#include <SDL2/SDL_scancode.h>
19
+#include <SDL2/SDL_stdinc.h>
20
+#include <SDL2/SDL_timer.h>
21
+#include <SDL2/SDL_video.h>
22
+
23
+
24
+/// Class definition
25
+
26
+class GLBackendSDL final : public GLBackend
27
+{
28
+
29
+public:
30
+
31
+    //// Special member functions
32
+
33
+    explicit GLBackendSDL(
34
+        std::string const & title,
35
+        std::array<int, 2>  size        = {0, 0},
36
+        std::array<int, 2>  version     = {0, 0},
37
+        int                 samples     = 0,
38
+        bool                fullscreen  = false,
39
+        bool                transparent = false
40
+    );
41
+    ~GLBackendSDL();
42
+    GLBackendSDL(GLBackendSDL &&) noexcept;
43
+    GLBackendSDL(GLBackendSDL const &) = delete;
44
+    GLBackendSDL & operator=(GLBackendSDL &&) = delete;
45
+    GLBackendSDL & operator=(GLBackendSDL const &) = delete;
46
+
47
+    //// Context
48
+
49
+    void current() override;
50
+
51
+    //// Render loop
52
+
53
+    void swap() override;
54
+
55
+    void events() override;
56
+
57
+    bool running() const override;
58
+    bool running(bool running) override;
59
+
60
+    float time() const override;
61
+    float time(float time) override;
62
+
63
+    //// Input and output
64
+
65
+    void lock(bool lock) override;
66
+
67
+    bool key   (std::string const & key)    const override;
68
+    bool button(int                 button) const override;
69
+
70
+    //// Debug
71
+
72
+    std::string debug_info() const override;
73
+
74
+protected:
75
+
76
+    //// Special member functions
77
+
78
+    void destroy_();
79
+
80
+protected:
81
+
82
+    //// Context
83
+
84
+    SDL_Window    * window_;
85
+    SDL_GLContext   context_;
86
+
87
+    //// Render loop
88
+
89
+    bool  running_;
90
+    float time_;
91
+
92
+};
93
+
94
+
95
+/// Inline definitions
96
+
97
+inline void GLBackendSDL::current()
98
+{
99
+    SDL_GL_MakeCurrent(window_, context_);
100
+}
101
+
102
+inline void GLBackendSDL::swap()
103
+{
104
+    SDL_GL_SwapWindow(window_);
105
+}
106
+
107
+inline bool GLBackendSDL::running(bool running)
108
+{
109
+    running_ = running;
110
+    return running;
111
+}
112
+
113
+inline bool GLBackendSDL::running() const
114
+{
115
+    return running_;
116
+}
117
+
118
+inline float GLBackendSDL::time() const
119
+{
120
+    // return (float)SDL_GetTicks() / 1000.0F - time_;
121
+    return
122
+        (float)SDL_GetPerformanceCounter() /
123
+        (float)SDL_GetPerformanceFrequency()
124
+        - time_;
125
+}
126
+
127
+inline float GLBackendSDL::time(float time)
128
+{
129
+    // time_ = (float)SDL_GetTicks() / 1000.0F - time;
130
+    time_ =
131
+        (float)SDL_GetPerformanceCounter() /
132
+        (float)SDL_GetPerformanceFrequency()
133
+        - time;
134
+    return time;
135
+}
136
+
137
+inline void GLBackendSDL::lock(bool lock)
138
+{
139
+    SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
140
+}
141
+
142
+inline bool GLBackendSDL::key(std::string const & key) const
143
+{
144
+    auto * keys = SDL_GetKeyboardState(nullptr);
145
+    if (key == "Enter")
146
+        return keys[SDL_SCANCODE_RETURN];
147
+    else if (key == "Control")
148
+        return keys[SDL_SCANCODE_RCTRL] || keys[SDL_SCANCODE_LCTRL];
149
+    else if (key == "Shift")
150
+        return keys[SDL_SCANCODE_RSHIFT] || keys[SDL_SCANCODE_LSHIFT];
151
+    else if (key == "Alt")
152
+        return keys[SDL_SCANCODE_RALT] || keys[SDL_SCANCODE_LALT];
153
+    else
154
+        return keys[SDL_GetScancodeFromName(key.c_str())];
155
+}
156
+
157
+inline bool GLBackendSDL::button(int button) const
158
+{
159
+    return SDL_GetMouseState(nullptr, nullptr) & (Uint32)SDL_BUTTON(button);
160
+}
161
+
162
+
163
+/// Guards
164
+
165
+#endif
166
+#endif
0 167
new file mode 100644
... ...
@@ -0,0 +1,304 @@
1
+/// Guards
2
+
3
+
4
+#ifdef GLBACKEND_SDL
5
+
6
+
7
+/// Includes
8
+
9
+
10
+#include <glbackend_sdl.hpp>
11
+
12
+#include <functional>
13
+#include <sstream>
14
+#include <string>
15
+#include <type_traits>
16
+#include <utility>
17
+
18
+#include <glbase.hpp>
19
+#include <glbackend.hpp>
20
+
21
+#include <SDL2/SDL.h>
22
+#include <SDL2/SDL_error.h>
23
+#include <SDL2/SDL_events.h>
24
+#include <SDL2/SDL_keycode.h>
25
+#include <SDL2/SDL_version.h>
26
+
27
+// NOLINTNEXTLINE
28
+#define STR_EXCEPTION GLBase::Exception
29
+#include <str.hpp>
30
+
31
+
32
+/// Constants
33
+
34
+
35
+auto static constexpr sdl_subsystems_ =
36
+    SDL_INIT_VIDEO  |
37
+    SDL_INIT_EVENTS |
38
+    SDL_INIT_TIMER;
39
+
40
+
41
+/// Special member functions
42
+
43
+
44
+GLBackendSDL::GLBackendSDL(
45
+    std::string const & title,
46
+    std::array<int, 2>  size,
47
+    std::array<int, 2>  version,
48
+    int                 samples,
49
+    bool                fullscreen,
50
+    bool                transparent
51
+)
52
+:
53
+    GLBackend(),
54
+    window_ {nullptr},
55
+    context_{nullptr},
56
+    running_{true},
57
+    time_   {0.0F}
58
+{
59
+    // Backend init
60
+    if (SDL_InitSubSystem(sdl_subsystems_) != 0)
61
+        STR_THROW(
62
+            "Failed to initialize SDL" << ":\n" <<
63
+            SDL_GetError()
64
+        );
65
+
66
+    // Window options
67
+    SDL_GL_ResetAttributes();
68
+    auto window_flags = Uint32{0};
69
+    window_flags |= SDL_WINDOW_OPENGL;
70
+    if (fullscreen)
71
+        window_flags |= SDL_WINDOW_FULLSCREEN;
72
+    if (transparent)
73
+    {
74
+        window_flags |= SDL_WINDOW_BORDERLESS;
75
+        window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
76
+    }
77
+    if (size[0] == 0 || size[1] == 0)
78
+    {
79
+        auto mode = SDL_DisplayMode{};
80
+        SDL_GetCurrentDisplayMode(0, &mode);
81
+        size[0] = mode.w;
82
+        size[1] = mode.h;
83
+    }
84
+    size_ = size;
85
+
86
+    // Context options
87
+    auto context_flags = int{0};
88
+    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, SDL_TRUE);
89
+    SDL_GL_SetAttribute(SDL_GL_RED_SIZE,      8); // NOLINT
90
+    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,    8); // NOLINT
91
+    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,     8); // NOLINT
92
+    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,    8); // NOLINT
93
+    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,   24); // NOLINT
94
+    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,  8); // NOLINT
95
+    if (samples)
96
+    {
97
+        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
98
+        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
99
+    }
100
+    if (version[0])
101
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, version[0]);
102
+    if (version[1])
103
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, version[1]);
104
+    if ((version[0] == 3 && version[1] >= 2) || version[0] >= 4) // NOLINT
105
+    {
106
+        SDL_GL_SetAttribute(
107
+            SDL_GL_CONTEXT_PROFILE_MASK,
108
+            SDL_GL_CONTEXT_PROFILE_CORE
109
+        );
110
+        context_flags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG; // NOLINT
111
+    }
112
+    if (debug() >= 1)
113
+        context_flags |= SDL_GL_CONTEXT_DEBUG_FLAG; // NOLINT
114
+    else if ((version[0] == 4 && version[1] >= 6) || version[0] >= 5) // NOLINT
115
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_NO_ERROR, SDL_TRUE);
116
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags);
117
+
118
+    try
119
+    {
120
+        // Window
121
+        window_ = SDL_CreateWindow(
122
+            title.c_str(),
123
+            SDL_WINDOWPOS_UNDEFINED, // NOLINT
124
+            SDL_WINDOWPOS_UNDEFINED, // NOLINT
125
+            size[0],
126
+            size[1],
127
+            window_flags
128
+        );
129
+        if (!window_)
130
+            STR_THROW(
131
+                "Failed to create SDL window" << ":\n" <<
132
+                SDL_GetError()
133
+            );
134
+
135
+        // Context
136
+        context_ = SDL_GL_CreateContext(window_);
137
+        if (!context_)
138
+            STR_THROW(
139
+                "Failed to create SDL context" << ":\n" <<
140
+                SDL_GetError()
141
+            );
142
+        SDL_GL_MakeCurrent(window_, context_);
143
+        if (debug() == 0)
144
+            SDL_GL_SetSwapInterval(1);
145
+        init_();
146
+
147
+        // Lock
148
+        if (fullscreen)
149
+            GLBackendSDL::lock(true);
150
+    }
151
+    catch (...)
152
+    {
153
+        destroy_();
154
+        throw;
155
+    }
156
+}
157
+
158
+
159
+GLBackendSDL::~GLBackendSDL()
160
+{
161
+    destroy_();
162
+}
163
+
164
+
165
+GLBackendSDL::GLBackendSDL(GLBackendSDL && other) noexcept
166
+:
167
+    GLBackend(std::move(other)),
168
+    window_ {other.window_},
169
+    context_{other.context_},
170
+    running_{other.running_},
171
+    time_   {other.time_}
172
+{
173
+    other.window_  = nullptr;
174
+    other.context_ = nullptr;
175
+}
176
+
177
+
178
+void GLBackendSDL::destroy_()
179
+{
180
+    GLBackendSDL::lock(false);
181
+    if (context_)
182
+        SDL_GL_DeleteContext(context_);
183
+    if (window_)
184
+        SDL_DestroyWindow(window_);
185
+    SDL_QuitSubSystem(sdl_subsystems_);
186
+}
187
+
188
+
189
+/// Render loop
190
+
191
+
192
+inline void GLBackendSDL::events()
193
+{
194
+    auto event = SDL_Event{};
195
+    while (SDL_PollEvent(&event))
196
+    {
197
+        switch (event.type)
198
+        {
199
+            case SDL_KEYDOWN:
200
+                if (event.key.repeat)
201
+                    break;
202
+                if (callback_key_)
203
+                {
204
+                    auto key = event.key.keysym.sym;
205
+                    if (key == SDLK_RETURN)
206
+                        callback_key_("Enter");
207
+                    else if (key == SDLK_LCTRL  || key == SDLK_RCTRL)
208
+                        callback_key_("Control");
209
+                    else if (key == SDLK_LSHIFT || key == SDLK_RSHIFT)
210
+                        callback_key_("Shift");
211
+                    else if (key == SDLK_LALT   || key == SDLK_RALT)
212
+                        callback_key_("Alt");
213
+                    else
214
+                        callback_key_(SDL_GetKeyName(key));
215
+                }
216
+                break;
217
+            case SDL_MOUSEBUTTONDOWN:
218
+                if (callback_button_)
219
+                    callback_button_(event.button.button);
220
+                break;
221
+            case SDL_MOUSEWHEEL:
222
+                scroll_ = {
223
+                    (float)event.wheel.x,
224
+                    (float)event.wheel.y,
225
+                };
226
+                if (callback_scroll_)
227
+                    callback_scroll_(scroll_);
228
+                break;
229
+            case SDL_MOUSEMOTION:
230
+                position_ = {
231
+                    (float)event.motion.x,
232
+                    (float)event.motion.y,
233
+                };
234
+                move_ = {
235
+                    (float)event.motion.xrel,
236
+                    (float)event.motion.yrel,
237
+                };
238
+                if (callback_position_)
239
+                    callback_position_(position_);
240
+                if (callback_move_)
241
+                    callback_move_(move_);
242
+                break;
243
+            case SDL_WINDOWEVENT:
244
+                switch (event.window.event)
245
+                {
246
+                    case SDL_WINDOWEVENT_SIZE_CHANGED:
247
+                        // size_ = {
248
+                        //     event.window.data1,
249
+                        //     event.window.data2,
250
+                        // };
251
+                        SDL_GL_GetDrawableSize(
252
+                            window_,
253
+                            &size_[0],
254
+                            &size_[1]
255
+                        );
256
+                        if (callback_size_)
257
+                            callback_size_(size_);
258
+                        break;
259
+                    case SDL_WINDOWEVENT_CLOSE:
260
+                            running_ = false;
261
+                        break;
262
+                }
263
+                break;
264
+        }
265
+    }
266
+}
267
+
268
+
269
+/// Debug
270
+
271
+
272
+std::string GLBackendSDL::debug_info() const
273
+{
274
+    auto ostream = std::ostringstream{};
275
+
276
+    auto version_compiled = SDL_version{};
277
+    auto version_linked   = SDL_version{};
278
+    SDL_VERSION(&version_compiled);
279
+    SDL_GetVersion(&version_linked);
280
+    debug_info_(ostream, "SDL", {
281
+        {
282
+            "VERSION_COMPILED",
283
+                std::to_string(version_compiled.major) + "." +
284
+                std::to_string(version_compiled.minor) + "." +
285
+                std::to_string(version_compiled.patch),
286
+        },
287
+        {
288
+            "VERSION_LINKED",
289
+                std::to_string(version_linked.major) + "." +
290
+                std::to_string(version_linked.minor) + "." +
291
+                std::to_string(version_linked.patch),
292
+        },
293
+    });
294
+
295
+    ostream << GLBackend::debug_info();
296
+
297
+    return ostream.str();
298
+}
299
+
300
+
301
+/// Guards
302
+
303
+
304
+#endif
0 305
new file mode 100644
... ...
@@ -0,0 +1,31 @@
1
+#ifdef GLBACKEND_SDL
2
+
3
+
4
+#include <SDL2/SDL.h>
5
+#include <SDL2/SDL_video.h>
6
+
7
+
8
+constexpr auto width = 640;
9
+constexpr auto height = 480;
10
+
11
+
12
+int main()
13
+{
14
+    if (SDL_Init(SDL_INIT_VIDEO) < 0)
15
+        return 1;
16
+    auto * window = SDL_CreateWindow(
17
+        "baseline_sdl",
18
+        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, // NOLINT
19
+        width, height,
20
+        SDL_WINDOW_OPENGL
21
+    );
22
+    if (!window)
23
+        return 1;
24
+    SDL_DestroyWindow(window);
25
+    SDL_Quit();
26
+}
27
+
28
+
29
+#else
30
+int main() {}
31
+#endif
0 32
new file mode 100644
... ...
@@ -0,0 +1,17 @@
1
+#ifdef GLBACKEND_SDL
2
+
3
+
4
+#include <glbackend_sdl.hpp>
5
+
6
+#include "common/run.hpp"
7
+
8
+
9
+int main()
10
+{
11
+    GLBACKEND_TESTS_COMMON_RUN(GLBackendSDL)
12
+}
13
+
14
+
15
+#else
16
+int main() {}
17
+#endif