1 /++ 2 + Authors: Avaxar <avaxar@nekkl.org> 3 + Copyright: Copyright © 2023, Avaxar 4 + License: $(LINK2 https://mit-license.org, MIT License) 5 +/ 6 7 module dsdl2.gl; 8 @safe: 9 10 import bindbc.sdl; 11 import dsdl2.sdl; 12 import dsdl2.window; 13 14 import core.memory : GC; 15 import std.conv : to; 16 import std.string : toStringz; 17 18 /++ 19 + Wraps `SDL_GL_LoadLibrary` which loads OpenGL from the given path of the library 20 + 21 + Params: 22 + path = path to the OpenGL library; `null` to load the default 23 + Throws: `dsdl2.SDLException` if unable to load the library 24 +/ 25 void loadGL(string path = null) @trusted { 26 if (SDL_GL_LoadLibrary(path is null ? null : path.toStringz()) != 0) { 27 throw new SDLException; 28 } 29 } 30 31 /++ 32 + Wraps `SDL_GL_GetProcAddress` which gets the pointer to a certain OpenGL function 33 + 34 + Params: 35 + proc = symbol name of the requested OpenGL function 36 + Returns: function pointer of the requested OpenGL function, otherwise `null` if not found 37 +/ 38 void* getGLProcAddress(string proc) @system { 39 return SDL_GL_GetProcAddress(proc.toStringz()); 40 } 41 42 /++ 43 + Wraps `SDL_GL_UnloadLibrary` which unloads the loaded OpenGL library from `dsdl2.loadGL` 44 +/ 45 void unloadGL() @trusted { 46 SDL_GL_UnloadLibrary(); 47 } 48 49 /++ 50 + Wraps `SDL_GL_ExtensionSupported` which checks whether an OpenGL extension is supported 51 + 52 + Params: 53 + extension = name of the OpenGL extension 54 + Returns: `true` if the extension is supported, otherwise `false` 55 +/ 56 bool isGLExtensionSupported(string extension) @trusted { 57 return SDL_GL_ExtensionSupported(extension.toStringz()) == SDL_TRUE; 58 } 59 60 /++ 61 + D enum that wraps `SDL_GLattr` defining OpenGL initialization attributes 62 +/ 63 enum GLAttribute { 64 /++ 65 + Wraps `SDL_GL_*` enumeration constants for `SDL_GLattr` 66 +/ 67 redSize = SDL_GL_RED_SIZE, 68 greenSize = SDL_GL_GREEN_SIZE, /// ditto 69 blueSize = SDL_GL_BLUE_SIZE, /// ditto 70 alphaSize = SDL_GL_ALPHA_SIZE, /// ditto 71 bufferSize = SDL_GL_BUFFER_SIZE, /// ditto 72 doubleBuffer = SDL_GL_DOUBLEBUFFER, /// ditto 73 depthSize = SDL_GL_DEPTH_SIZE, /// ditto 74 stencilSize = SDL_GL_STENCIL_SIZE, /// ditto 75 accumRedSize = SDL_GL_ACCUM_RED_SIZE, /// ditto 76 accumGreenSize = SDL_GL_ACCUM_GREEN_SIZE, /// ditto 77 accumBlueSize = SDL_GL_ACCUM_BLUE_SIZE, /// ditto 78 accumAlphaSize = SDL_GL_ACCUM_ALPHA_SIZE, /// ditto 79 stereo = SDL_GL_STEREO, /// ditto 80 multiSampleBuffers = SDL_GL_MULTISAMPLEBUFFERS, /// ditto 81 multiSampleSamples = SDL_GL_MULTISAMPLESAMPLES, /// ditto 82 acceleratedVisual = SDL_GL_ACCELERATED_VISUAL, /// ditto 83 contextMajorVersion = SDL_GL_CONTEXT_MAJOR_VERSION, /// ditto 84 contextMinorVersion = SDL_GL_CONTEXT_MINOR_VERSION, /// ditto 85 // contextFlags = SDL_GL_CONTEXT_FLAGS, /// ditto 86 contextProfileMask = SDL_GL_CONTEXT_PROFILE_MASK, /// ditto 87 shareWithCurrentContext = SDL_GL_SHARE_WITH_CURRENT_CONTEXT /// ditto 88 // framebufferSRGBCapable = SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, /// ditto 89 // contextReleaseBehavior = SDL_GL_CONTEXT_RELEASE_BEHAVIOR /// ditto 90 } 91 92 /++ 93 + D enum that wraps `SDL_GLprofile` defining OpenGL profiles 94 +/ 95 enum GLProfile : uint { 96 /++ 97 + Wraps `SDL_GL_CONTEXT_*` enumeration constants 98 +/ 99 core = SDL_GL_CONTEXT_PROFILE_CORE, 100 compatibility = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY, /// ditto 101 es = SDL_GL_CONTEXT_PROFILE_ES /// ditto 102 } 103 104 /++ 105 + Wraps `SDL_GL_SetAttribute` which sets OpenGL attributes for initialization 106 + 107 + Params: 108 + attribute = the `dsdl2.GLAttribute` to set 109 + value = requested value for the attribute to be set as 110 + Throws: `dsdl2.SDLException` if unable to set the attribute 111 +/ 112 void setGLAttribute(GLAttribute attribute, uint value) @trusted { 113 if (SDL_GL_SetAttribute(attribute, value.to!int) != 0) { 114 throw new SDLException; 115 } 116 } 117 118 /++ 119 + Wraps `SDL_GL_GetAttribute` which gets previously set OpenGL attributes for initialization 120 + 121 + Params: 122 + attribute = the `dsdl2.GLAttribute` whose value to get 123 + Returns: value of the requested attribute 124 + Throws: `dsdl2.SDLException` if unable to get the attribute 125 +/ 126 uint getGLAttribute(GLAttribute attribute) @trusted { 127 uint value = void; 128 if (SDL_GL_GetAttribute(attribute, cast(int*)&value) != 0) { 129 throw new SDLException; 130 } 131 132 return value; 133 } 134 135 static if (sdlSupport >= SDLSupport.v2_0_2) { 136 /++ 137 + Wraps `SDL_GL_ResetAttributes` (from SDL 2.0.2) which resets all OpenGL attributes previously set to default 138 +/ 139 void resetGLAttributes() @trusted 140 in { 141 assert(getVersion() >= Version(2, 0, 2)); 142 } 143 do { 144 SDL_GL_ResetAttributes(); 145 } 146 } 147 148 /++ 149 + Wraps `SDL_GL_GetCurrentWindow` which gets the current target window for OpenGL 150 + 151 + This function is marked as `@system` due to the potential of referencing invalid memory. 152 + 153 + Returns: `dsdl2.Window` proxy to the target window for OpenGL 154 + Throws: `dsdl2.SDLException` if unable to get the window 155 +/ 156 Window getCurrentGLWindow() @system { 157 if (SDL_Window* sdlWindow = SDL_GL_GetCurrentWindow()) { 158 return new Window(sdlWindow, false); 159 } 160 else { 161 throw new SDLException; 162 } 163 } 164 165 /++ 166 + Wraps `SDL_GL_GetCurrentContext` which gets the current OpenGL context used by SDL 167 + 168 + This function is marked as `@system` due to the potential of referencing invalid memory. 169 + 170 + Returns: `dsdl2.GLContext` proxy to the OpenGL context used by SDL 171 + Throws: `dsdl2.SDLException` if unable to get the context 172 +/ 173 GLContext getCurrentGLContext() @system { 174 if (SDL_GLContext sdlGLContext = SDL_GL_GetCurrentContext()) { 175 return new GLContext(sdlGLContext, false); 176 } 177 else { 178 throw new SDLException; 179 } 180 } 181 182 /++ 183 + D enum that defines swap intervals for OpenGL 184 +/ 185 enum GLSwapInterval { 186 immediate = 0, /// No vertical retrace synchronization 187 syncWithVerticalRetrace = 1, /// The buffer swap is synchronized with the vertical retrace 188 adaptiveVSync = -1 /// Late swaps happen immediately instead of waiting for the next retrace 189 } 190 191 /++ 192 + Wraps `SDL_GL_SetSwapInterval` which gets the swap interval method for OpenGL 193 + 194 + Params: 195 + interval = the `dsdl2.GLSwapInterval` method to use 196 + Throws: `dsdl2.SDLException` if unable to set the swap interval 197 +/ 198 void setGLSwapInterval(GLSwapInterval interval) @trusted { 199 if (SDL_GL_SetSwapInterval(cast(int) interval) != 0) { 200 throw new SDLException; 201 } 202 } 203 204 /++ 205 + Wraps `SDL_GL_GetSwapInterval` which gets the currently-used swap interval method for OpenGL 206 + 207 + Returns: the currently-used `dsdl2.GLSwapInterval` method 208 + Throws: `dsdl2.SDLException` if unable to get the swap interval 209 +/ 210 GLSwapInterval getGLSwapInterval() @trusted { 211 // NOTE: This function is able to return an error. But the docs aren't clear when. 212 return cast(GLSwapInterval) SDL_GL_GetSwapInterval(); 213 } 214 215 /++ 216 + D class that wraps `SDL_GLContext` enclosing an OpenGL context used for SDL 217 +/ 218 class GLContext { 219 private bool isOwner = true; 220 private void* userRef = null; 221 222 @system SDL_GLContext sdlGLContext = null; /// Internal `SDL_GLContext` 223 224 /++ 225 + Constructs a `dsdl2.GLContext` from a vanilla `SDL_GLContext` from bindbc-sdl 226 + 227 + Params: 228 + sdlGLContext = the `SDL_GLContext` to manage 229 + isOwner = whether the instance owns the given `SDL_GLContext` and should destroy it on its own 230 + userRef = optional pointer to maintain reference link, avoiding GC cleanup 231 +/ 232 this(SDL_GLContext sdlGLContext, bool isOwner = true, void* userRef = null) @system 233 in { 234 assert(sdlGLContext !is null); 235 } 236 do { 237 this.sdlGLContext = sdlGLContext; 238 this.isOwner = isOwner; 239 this.userRef = userRef; 240 } 241 242 /++ 243 + Creates an OpenGL context to use by SDL, which wraps `SDL_GL_CreateContext` 244 + 245 + Params: 246 + window = the default OpenGL `dsdl2.Window` to be set current as the rendering target for the context 247 + Throws: `dsdl2.SDLException` if OpenGL context creation failed 248 +/ 249 this(Window window) @trusted 250 in { 251 assert(window !is null); 252 } 253 do { 254 this.sdlGLContext = SDL_GL_CreateContext(window.sdlWindow); 255 if (this.sdlGLContext is null) { 256 throw new SDLException; 257 } 258 } 259 260 ~this() @trusted { 261 if (this.isOwner) { 262 SDL_GL_DeleteContext(this.sdlGLContext); 263 } 264 } 265 266 @trusted invariant { // @suppress(dscanner.trust_too_much) 267 // Instance might be in an invalid state due to holding a non-owned externally-freed object when 268 // destructed in an unpredictable order. 269 if (!this.isOwner && GC.inFinalizer) { 270 return; 271 } 272 273 assert(this.sdlGLContext !is null); 274 } 275 276 /++ 277 + Wraps `SDL_GL_MakeCurrent` which makes a window current as the rendering target for OpenGL rendering 278 + 279 + Params: 280 + window = new OpenGL `dsdl2.Window` to be set current as the rendering target for the context 281 + Throws: `dsdl2.SDLException` if failed to set the new window current 282 +/ 283 void makeCurrent(Window window) @trusted 284 in { 285 assert(window !is null); 286 } 287 do { 288 if (SDL_GL_MakeCurrent(window.sdlWindow, this.sdlGLContext) != 0) { 289 throw new SDLException; 290 } 291 } 292 }