Skip to content

Commit 0bca55a

Browse files
authored
Add function for app to explicitly make libktx load its GL function pointers. (#894)
Fixes #708.
1 parent cc56485 commit 0bca55a

File tree

8 files changed

+125
-58
lines changed

8 files changed

+125
-58
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ set(BASISU_ENCODER_CXX_SRC
454454

455455
if(KTX_FEATURE_GL_UPLOAD)
456456
list(APPEND KTX_MAIN_SRC
457+
lib/gl_funclist.inl
457458
lib/gl_funcs.c
458459
lib/gl_funcs.h
459460
lib/glloader.c

cmake/docs.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ function( CreateDocLibKTX )
122122
lib/basis_transcode.cpp
123123
lib/miniz_wrapper.cpp
124124
lib/strings.c
125+
lib/gl_funcs.c
125126
lib/glloader.c
126127
lib/hashlist.c
127128
lib/filestream.c

include/ktx.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,22 @@ struct ktxStream
905905
* functions.
906906
*/
907907

908+
/**
909+
* @~English
910+
* @brief typedef of function pointer returned by GLGetProcAddress functions.
911+
*/
912+
typedef void (KTX_APIENTRY* PFNVOIDFUNCTION)(void);
913+
/**
914+
* @~English
915+
* @brief typedef of pointer to function for retrieving OpenGL function pointers.
916+
*/
917+
typedef PFNVOIDFUNCTION (KTX_APIENTRY* PFNGLGETPROCADDRESS) (const char *proc);
918+
/*
919+
* Load pointers for the OpenGL functions needed by ktxTexture_GLUpload.
920+
*/
921+
KTX_API KTX_error_code KTX_APIENTRY
922+
ktxLoadOpenGL(PFNGLGETPROCADDRESS pfnGLGetProcAddress);
923+
908924
/*
909925
* These four create a ktxTexture1 or ktxTexture2 according to the data
910926
* header, and return a pointer to the base ktxTexture class.

lib/gl_funcs.c

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -52,69 +52,63 @@
5252
#else
5353
#include <dlfcn.h>
5454
#include <stdlib.h>
55-
#define WINAPI
5655
#endif
5756
#define NO_SHORTCUTS
5857
#include "gl_funcs.h"
5958

6059
#if WINDOWS
6160
#define GetOpenGLModuleHandle(flags) ktxFindOpenGL()
62-
#define LoadProcAddr GetProcAddress
63-
HMODULE ktxOpenGLModuleHandle;
61+
static HMODULE ktxOpenGLModuleHandle;
62+
static PFNGLGETPROCADDRESS pfnWglGetProcAddress;
63+
64+
PFNVOIDFUNCTION
65+
defaultGLGetProcAddress(const char* proc)
66+
{
67+
PFNVOIDFUNCTION pfnGLProc = NULL;
68+
69+
if (pfnWglGetProcAddress)
70+
pfnGLProc = pfnWglGetProcAddress(proc);
71+
if (!pfnGLProc) {
72+
pfnGLProc = (PFNVOIDFUNCTION)GetProcAddress(ktxOpenGLModuleHandle,
73+
proc);
74+
}
75+
return pfnGLProc;
76+
}
6477
#elif MACOS || UNIX || IOS
6578
// Using NULL returns a handle that can be used to search the process that
6679
// loaded us and any other libraries it has loaded. That's all we need to
6780
// search as the app is responsible for creating the GL context so it must
6881
// be there.
6982
#define GetOpenGLModuleHandle(flags) dlopen(NULL, flags)
70-
#define LoadProcAddr dlsym
71-
#define LIBRARY_NAME NULL
72-
void* ktxOpenGLModuleHandle;
83+
static void* ktxOpenGLModuleHandle;
84+
85+
PFNVOIDFUNCTION
86+
defaultGLGetProcAddress(const char* proc)
87+
{
88+
return dlsym(ktxOpenGLModuleHandle, proc);
89+
}
7390
#elif WEB
7491
extern void* emscripten_GetProcAddress(const char *name_);
7592
#define GetOpenGLModuleHandle(flag) (void*)0x0000ffff // Value doesn't matter.
76-
#define LoadProcAddr(lib, proc) emscripten_GetProcAddress(proc)
77-
#define LIBRARY_NAME "unused"
7893
void* ktxOpenGLModuleHandle;
94+
95+
#define defaultGLGetProcAddress ((PFNGLGETPROCADDRESS)emscripten_GetProcAddress)
7996
#else
8097
#error "Don\'t know how to load symbols on this OS."
8198
#endif
8299

83-
typedef void (WINAPI *PFNVOIDFUNCTION)(void);
84-
typedef PFNVOIDFUNCTION *(WINAPI * PFNWGLGETPROCADDRESS) (const char *proc);
85-
static PFNWGLGETPROCADDRESS wglGetProcAddressPtr;
100+
static bool openGLLoaded = false;
86101
static const char* noloadmsg = "Could not load OpenGL command: %s!\n";
87102

88103
/* Define pointers for functions libktx is using. */
89104
struct glFuncPtrs gl;
90105

91-
#if defined(__GNUC__)
92-
// This strange casting is because dlsym returns a void* thus is not
93-
// compatible with ISO C which forbids conversion of object pointers
94-
// to function pointers. The cast masks the conversion from the
95-
// compiler thus no warning even though -pedantic is set. Since the
96-
// platform supports dlsym, conversion to function pointers must
97-
// work, despite the mandated ISO C warning.
98106
#define GL_FUNCTION(type, func, required) \
99-
if ( wglGetProcAddressPtr ) \
100-
*(void **)(&gl.func) = wglGetProcAddressPtr(#func); \
101-
if ( !gl.func ) \
102-
*(void **)(&gl.func) = LoadProcAddr(ktxOpenGLModuleHandle, #func); \
103-
if ( !gl.func && required ) { \
104-
fprintf(stderr, noloadmsg, #func); \
105-
return KTX_NOT_FOUND; \
106-
}
107-
#else
108-
#define GL_FUNCTION(type, func, required) \
109-
if ( wglGetProcAddressPtr ) \
110-
gl.func = (type)wglGetProcAddressPtr(#func); \
111-
if ( !gl.func) \
112-
gl.func = (type)LoadProcAddr(ktxOpenGLModuleHandle, #func); \
107+
gl.func = (type)pfnGLGetProcAddress(#func); \
113108
if ( !gl.func && required) { \
114109
fprintf(stderr, noloadmsg, #func); \
115110
return KTX_NOT_FOUND; \
116111
}
117-
#endif
118112

119113
#if WINDOWS
120114
static HMODULE
@@ -130,7 +124,7 @@ ktxFindOpenGL() {
130124
&module
131125
);
132126
if (found) {
133-
if (LoadProcAddr(module, "glGetError") != NULL)
127+
if (GetProcAddress(module, "glGetError") != NULL)
134128
return module;
135129
}
136130
// Not statically linked. See what dll the process has loaded.
@@ -154,22 +148,23 @@ ktxFindOpenGL() {
154148
);
155149
if (found) {
156150
// Need wglGetProcAddr for non-OpenGL-2 functions.
157-
wglGetProcAddressPtr =
158-
(PFNWGLGETPROCADDRESS)LoadProcAddr(module,
159-
"wglGetProcAddress");
160-
if (wglGetProcAddressPtr != NULL)
151+
pfnWglGetProcAddress =
152+
(PFNGLGETPROCADDRESS)GetProcAddress(module,
153+
"wglGetProcAddress");
154+
if (pfnWglGetProcAddress != NULL)
161155
return module;
162156
}
163-
return module; // Keep the compiler happy!
157+
return 0;
164158
}
165159
#endif
166160

167161
ktx_error_code_e
168162
ktxLoadOpenGLLibrary(void)
169163
{
170-
if (ktxOpenGLModuleHandle)
164+
if (openGLLoaded)
171165
return KTX_SUCCESS;
172166

167+
// Look for OpenGL module and set up default GetProcAddress.
173168
ktxOpenGLModuleHandle = GetOpenGLModuleHandle(RTLD_LAZY);
174169
if (ktxOpenGLModuleHandle == NULL) {
175170
fprintf(stderr, "OpenGL lib not linked or loaded by application.\n");
@@ -184,9 +179,48 @@ ktxLoadOpenGLLibrary(void)
184179
return KTX_LIBRARY_NOT_LINKED; // So release version doesn't crash.
185180
#endif
186181
}
182+
return KTX_SUCCESS;
183+
}
184+
185+
/**
186+
* @~English
187+
* @brief Load pointers for the GL functions used by ktxTexture_GLUpload.
188+
*
189+
* Should be called by an application before its first call to
190+
* ktxTexture\_GLUpload, passing a pointer to the GLGetProcAddress function
191+
* provided by whatever OpenGL framework it is using. For backward
192+
* compatibility, ktxTexture\_GLUpload calls this with a NULL pointer causing an
193+
* attempt to load the pointers from the program module using
194+
* @c dlsym (GNU/Linux, macOS), @c wglGetProcAddr and @c GetProcAddr (Windows)
195+
* or @c emscripten_GetProcAddress (Web). This works with the vast majority of
196+
* OpenGL implementations but issues have been seen on Fedora systems
197+
* particularly with NVIDIA hardware. For full robustness, applications should
198+
* call this function.
199+
*
200+
* @param [in] pfnGLGetProcAddress pointer to function for retrieving pointers
201+
* to GL functions. If NULL, retrieval is
202+
* attempted using system dependent generic
203+
* functions.
204+
*/
205+
KTX_API ktx_error_code_e KTX_APIENTRY
206+
ktxLoadOpenGL(PFNGLGETPROCADDRESS pfnGLGetProcAddress)
207+
{
208+
if (openGLLoaded)
209+
return KTX_SUCCESS;
210+
211+
if (!pfnGLGetProcAddress) {
212+
ktx_error_code_e result = ktxLoadOpenGLLibrary();
213+
if (result != KTX_SUCCESS) {
214+
return result;
215+
}
216+
pfnGLGetProcAddress = defaultGLGetProcAddress;
217+
}
218+
219+
// Load function pointers
187220

188221
#include "gl_funclist.inl"
189222

223+
openGLLoaded = true;
190224
return KTX_SUCCESS;
191225
}
192226

lib/gl_funcs.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@
2424
#include "GL/glcorearb.h"
2525
#include "ktx.h"
2626

27-
#if WINDOWS
28-
#define WINDOWS_LEAN_AND_MEAN
29-
#include <windows.h>
30-
extern HMODULE ktxOpenGLModuleHandle;
31-
#else
32-
extern void* ktxOpenGLModuleHandle;
33-
#endif
34-
3527
extern ktx_error_code_e ktxLoadOpenGLLibrary(void);
3628

3729
#define GL_FUNCTION(type, fun, required) type fun;

lib/glloader.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -924,11 +924,9 @@ ktxTexture1_GLUpload(ktxTexture1* This, GLuint* pTexture, GLenum* pTarget,
924924
return KTX_INVALID_VALUE;
925925
}
926926

927-
if (!ktxOpenGLModuleHandle) {
928-
result = ktxLoadOpenGLLibrary();
929-
if (result != KTX_SUCCESS) {
930-
return result;
931-
}
927+
result = ktxLoadOpenGL(NULL);
928+
if (result != KTX_SUCCESS) {
929+
return result;
932930
}
933931
/* KTX 1 files require an unpack alignment of 4 */
934932
glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousUnpackAlignment);
@@ -1010,11 +1008,9 @@ ktxTexture2_GLUpload(ktxTexture2* This, GLuint* pTexture, GLenum* pTarget,
10101008
return KTX_INVALID_VALUE;
10111009
}
10121010

1013-
if (!ktxOpenGLModuleHandle) {
1014-
result = ktxLoadOpenGLLibrary();
1015-
if (result != KTX_SUCCESS) {
1016-
return result;
1017-
}
1011+
result = ktxLoadOpenGL(NULL);
1012+
if (result != KTX_SUCCESS) {
1013+
return result;
10181014
}
10191015

10201016
if (This->vkFormat != VK_FORMAT_UNDEFINED) {

lib/libktx_mainpage.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ GLenum target, glerror;
8484
result = ktxTexture_CreateFromNamedFile("mytex3d.ktx",
8585
KTX_TEXTURE_CREATE_NO_FLAGS,
8686
&kTexture);
87+
88+
// Before the first call to ktxTexture_GLUpload make libktx load its
89+
// function pointers for the GL functions it uses. The parameter is a
90+
// pointer to the GLGetProcAddress function provided by whatever OpenGL
91+
// framework the application is using. The example shown is for SDL.
92+
//
93+
// Note 1: This is unrelated to any GL function pointers the app may be
94+
// using.
95+
// Note 2: When this is not called, libktx has fallback mechanisms to
96+
// find the pointers which work on the vast majority of
97+
// platforms. The only known failures have occurred on Fedora.
98+
result = ktxLoadOpenGL((PFNGLGETPROCADDRESS)SDL_GL_GetProcAddress);
99+
87100
glGenTextures(1, &texture); // Optional. GLUpload can generate a texture.
88101
result = ktxTexture_GLUpload(kTexture, &texture, &target, &glerror);
89102
ktxTexture_Destroy(kTexture);

tests/loadtests/glloadtests/GLLoadTests.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,23 @@ GLLoadTests::~GLLoadTests()
4141
bool
4242
GLLoadTests::initialize(Args& args)
4343
{
44+
bool explicitlyLoadOpenGL = false;
45+
for (uint32_t i = 1; i < args.size(); i++) {
46+
if (args[i].compare("--load-gl") == 0) {
47+
explicitlyLoadOpenGL = true;
48+
args.erase(args.begin() + i);
49+
break;
50+
}
51+
}
52+
4453
if (!GLAppSDL::initialize(args))
4554
return false;
4655

56+
// --load-gl allows testing of explicitly loading the OpenGL pointers.
57+
// Default is for ktxTexture_GLUpload to implicitly load them.
58+
if (explicitlyLoadOpenGL)
59+
ktxLoadOpenGL(reinterpret_cast<PFNGLGETPROCADDRESS>(SDL_GL_GetProcAddress));
60+
4761
for (auto it = args.begin() + 1; it != args.end(); it++) {
4862
infiles.push_back(*it);
4963
}

0 commit comments

Comments
 (0)