105105#include <stdlib.h>
106106#include <string.h>
107107
108+
109+ /* thread-safe init/caches when C11 atomics are available. */
110+ #if !defined(__STDC_NO_ATOMICS__ )
111+ #define SOIL2_HAVE_C11_ATOMICS 1
112+ #include <stdatomic.h>
113+ #else
114+ #define SOIL2_HAVE_C11_ATOMICS 0
115+ #endif
108116// --- projectM patched GL discovery start ---
109117
110118#if defined(_WIN32 ) || defined(SOIL_PLATFORM_WIN32 )
@@ -206,43 +214,57 @@ static void soil2_init_stringi(void)
206214// --- projectM patched GL discovery end ---
207215
208216#if defined( SOIL_X11_PLATFORM ) || defined( SOIL_PLATFORM_WIN32 ) || defined( SOIL_PLATFORM_OSX ) || defined(__HAIKU__ )
209- typedef const GLubyte * (APIENTRY * P_SOIL_glGetStringiFunc ) (GLenum , GLuint );
210217
211218// --- projectM patched GL discovery start ---
212219
213220static int isAtLeastGL3 ()
214221{
222+ /* Cache result. If atomics are available, this is thread-safe. */
223+ #if SOIL2_HAVE_C11_ATOMICS
224+ static atomic_int cached = ATOMIC_VAR_INIT (SOIL_CAPABILITY_UNKNOWN );
225+ int cached_val = atomic_load_explicit (& cached , memory_order_acquire );
226+ if (cached_val != SOIL_CAPABILITY_UNKNOWN )
227+ return cached_val ;
228+ #else
215229 static int cached = SOIL_CAPABILITY_UNKNOWN ;
216- if (cached != SOIL_CAPABILITY_UNKNOWN ) return cached ;
230+ if (cached != SOIL_CAPABILITY_UNKNOWN )
231+ return cached ;
232+ #endif
217233
218234 const char * verstr = (const char * )glGetString (GL_VERSION );
219- if (!verstr ) {
235+ if (!verstr )
236+ {
237+ #if SOIL2_HAVE_C11_ATOMICS
238+ atomic_store_explicit (& cached , SOIL_CAPABILITY_NONE , memory_order_release );
239+ return SOIL_CAPABILITY_NONE ;
240+ #else
220241 cached = SOIL_CAPABILITY_NONE ;
221242 return cached ;
243+ #endif
222244 }
223245
224- // Desktop examples:
225- // "3.3.0 NVIDIA 555.xx"
226- // GLES examples:
227- // "OpenGL ES 3.2 Mesa ..."
228- // "OpenGL ES-CM 1.1 ..." (old)
229246 const int is_es = (strstr (verstr , "OpenGL ES" ) != NULL ) || (strstr (verstr , " ES " ) != NULL );
230247
248+ int result = SOIL_CAPABILITY_NONE ;
231249 int maj = 0 , min = 0 ;
232- if (is_es ) {
233- // Best-effort: parse the first X.Y in the string
250+ if (is_es )
251+ {
234252 if (sscanf (verstr , "%*[^0-9]%d.%d" , & maj , & min ) == 2 && maj >= 3 )
235- cached = SOIL_CAPABILITY_PRESENT ;
236- else
237- cached = SOIL_CAPABILITY_NONE ;
238- } else {
253+ result = SOIL_CAPABILITY_PRESENT ;
254+ }
255+ else
256+ {
239257 if (sscanf (verstr , "%d.%d" , & maj , & min ) == 2 && maj >= 3 )
240- cached = SOIL_CAPABILITY_PRESENT ;
241- else
242- cached = SOIL_CAPABILITY_NONE ;
258+ result = SOIL_CAPABILITY_PRESENT ;
243259 }
244260
261+ #if SOIL2_HAVE_C11_ATOMICS
262+ atomic_store_explicit (& cached , result , memory_order_release );
263+ return result ;
264+ #else
265+ cached = result ;
245266 return cached ;
267+ #endif
246268}
247269// --- projectM patched GL discovery end ---
248270
@@ -335,13 +357,35 @@ typedef void (APIENTRY * P_SOIL_GLGENERATEMIPMAPPROC)(GLenum target);
335357static P_SOIL_GLGENERATEMIPMAPPROC soilGlGenerateMipmap = NULL ;
336358
337359
360+ #if SOIL2_HAVE_C11_ATOMICS
361+ /* 0=uninitialized, 1=initializing, 2=initialized */
362+ static atomic_int soil2GlInitState = ATOMIC_VAR_INIT (0 );
363+ #else
338364static int soil2GlInitialized = 0 ;
365+ #endif
339366
340367void SOIL_GL_Init ()
341368{
342369 /* Must be called after a GL context exists and AFTER SOIL_GL_SetResolver(). */
370+ #if SOIL2_HAVE_C11_ATOMICS
371+ int state = atomic_load_explicit (& soil2GlInitState , memory_order_acquire );
372+ if (state == 2 )
373+ return ;
374+
375+ int expected = 0 ;
376+ if (!atomic_compare_exchange_strong_explicit (& soil2GlInitState , & expected , 1 ,
377+ memory_order_acq_rel , memory_order_acquire ))
378+ {
379+ while ((state = atomic_load_explicit (& soil2GlInitState , memory_order_acquire )) == 1 )
380+ {
381+ /* spin */
382+ }
383+ return ;
384+ }
385+ #else
343386 if (soil2GlInitialized )
344387 return ;
388+ #endif
345389
346390 /* Resolve compressed upload */
347391 soilGlCompressedTexImage2D =
@@ -366,12 +410,20 @@ void SOIL_GL_Init()
366410 (P_SOIL_GLGENERATEMIPMAPPROC )SOIL_GL_GetProcAddress ("glGenerateMipmapOES" );
367411 }
368412
413+ #if SOIL2_HAVE_C11_ATOMICS
414+ atomic_store_explicit (& soil2GlInitState , 2 , memory_order_release );
415+ #else
369416 soil2GlInitialized = 1 ;
417+ #endif
370418}
371419
372420void SOIL_GL_Destroy ()
373421{
422+ #if SOIL2_HAVE_C11_ATOMICS
423+ atomic_store_explicit (& soil2GlInitState , 0 , memory_order_release );
424+ #else
374425 soil2GlInitialized = 0 ;
426+ #endif
375427 soilGlCompressedTexImage2D = NULL ;
376428 soilGlGenerateMipmap = NULL ;
377429 soilGlGetStringi = NULL ;
@@ -381,10 +433,17 @@ void SOIL_GL_Destroy()
381433/* Ensure OpenGL function pointers are initialized (safe to call repeatedly). */
382434static void soil2_ensure_gl_initialized (void )
383435{
384- if (!soil2GlInitialized )
385- {
386- SOIL_GL_Init ();
387- }
436+ #if SOIL2_HAVE_C11_ATOMICS
437+ if (atomic_load_explicit (& soil2GlInitState , memory_order_acquire ) != 2 )
438+ {
439+ SOIL_GL_Init ();
440+ }
441+ #else
442+ if (!soil2GlInitialized )
443+ {
444+ SOIL_GL_Init ();
445+ }
446+ #endif
388447}
389448
390449// --- projectM patched GL discovery end ---
0 commit comments