Hi,
I was wondering how to access the OpenGL ARB_imaging functions from withing Croquet. Whenever I try, I get the error "Unable to find function address". I used glGetString(GL_EXTENSIONS) to verify that GL_ARB_imaging is supported. I thing that the problem is the opengl32.dll doesn't provide the appropriate hooks (stupid Microsoft), even though NVIDIA's driver implements the functionality. I tried substituting the library "nvoglnt.dll" for "opengl32.dll", because the "strings" command told me that glMinMax appeared in the former file, which is part of the NVIDIA driver. This didn't work either, which isn't surprising, since it was basically a guess.
Other ideas? Joshua
Am Samstag, 21.12.02 um 22:09 Uhr schrieb Joshua 'Schwa' Gargus:
I was wondering how to access the OpenGL ARB_imaging functions from withing Croquet. Whenever I try, I get the error "Unable to find function address". I used glGetString(GL_EXTENSIONS) to verify that GL_ARB_imaging is supported. I thing that the problem is the opengl32.dll doesn't provide the appropriate hooks (stupid Microsoft), even though NVIDIA's driver implements the functionality.
For all OpenGL extension functions, you have to get a function pointer yourself using the wglGetProcAddress() function. (glXGetProcAddress on X, don't know on Mac). On Windows, you even have to get the OpenGL 1.3 and 1.4 functions this way since Microsoft did not update their bindings in years.
-- Bert
Thanks Bert,
I'm still not comfortable with FFI stuff, and my NuBlue book is back in Atlanta. I suppose that I have to get an ExternalLibraryFunction for wglGetProcAddress(), which I should then invoke to get the functions I need.
(giving it a try)
I gave this a try, but was unsuccessful. The following code (executed in a workspace) resulted in an ExternalLibraryFunction with a null handle.
func _ ExternalLibraryFunction name: 'glGetString' module: 'opengl32.dll' callType: ExternalFunction callTypeAPI "callTypeCDecl" returnType: ExternalType string argumentTypes: {ExternalType long}.
I tried it with both call types; neither worked. Ideas, anyone?
Thanks again, Joshua
On Sat, Dec 21, 2002 at 11:15:22PM +0100, Bert Freudenberg wrote:
Am Samstag, 21.12.02 um 22:09 Uhr schrieb Joshua 'Schwa' Gargus:
I was wondering how to access the OpenGL ARB_imaging functions from withing Croquet. Whenever I try, I get the error "Unable to find function address". I used glGetString(GL_EXTENSIONS) to verify that GL_ARB_imaging is supported. I thing that the problem is the opengl32.dll doesn't provide the appropriate hooks (stupid Microsoft), even though NVIDIA's driver implements the functionality.
For all OpenGL extension functions, you have to get a function pointer yourself using the wglGetProcAddress() function. (glXGetProcAddress on X, don't know on Mac). On Windows, you even have to get the OpenGL 1.3 and 1.4 functions this way since Microsoft did not update their bindings in years.
-- Bert
Am Sonntag, 22.12.02 um 00:01 Uhr schrieb Joshua 'Schwa' Gargus:
Thanks Bert,
I'm still not comfortable with FFI stuff, and my NuBlue book is back in Atlanta. I suppose that I have to get an ExternalLibraryFunction for wglGetProcAddress(), which I should then invoke to get the functions I need.
(giving it a try)
I gave this a try, but was unsuccessful. Ideas, anyone?
Didn't Andreas' hack come through? It's cute!
-- Bert
Be Gentle - its my first submission and the first bit of Smalltalk I've done in years.
This is basically David T Lewis's TimeZoneDatabase stuff with some extra support for prompting the user to select a timezone and extra logic that only loads all the timezones when the user is prompted to select a timezone. Otherwise it only keeps two zones in memory - yours and the Zulu zone. In addition, there is a bunch of glue that consists of:
* Added ISO 8601 read and write support to Date and Time. * This package also includes a modified version of the TimeStamp class that comes with SqueakMap. The new TimeStamp has been gutted and made timezone aware. It is entirely implemented in terms of PointInTime. Thus, if you change your timezone, all of your timestamps will update accordingly.
TimeStamp also has support for reading/writing ISO 8601 format timestamps and consequently it is now safe to compare timestamps that were generated on different machines because they all reference the same base time.
Hi Joshua,
You are raising an issue that I haven't investigated yet deeply enough to give you any definitive answers. Here's what I can say for sure: OpenGL extensions have to be looked up dynamically and the lookup mechanisms are different from your "regular OS lookup". I think that's because different renderers may expose different extensions and therefore the extensions are really renderer specific. In order to call an extension you will have to use wglGetProcAddress (on Windows) which will answer the address of the function (if it exists) or zero otherwise. This should look like:
wglGetProcAddress: lpszProc <apicall: ulong 'wglGetProcAddress' (char*) module: 'opengl32.dll'> ^self externalCallFailed
and needs to be used as in
glx wglGetProcAddress: 'glResetHistogram'.
The problem is that this only tells you the address of the function but you cannot invoke "an address" - you need a fully spec-ed out external function in order to use it. The external function can be constructed as in:
"declare glResetHistogram" meth := ExternalLibraryFunction "name+module are omitted since we get the handle by other means" name:'' module: '' "calling convention" callType: ExternalFunction callTypeAPI "return type" returnType: ExternalType void "argument types" argumentTypes: (Array with: ExternalType ulong).
"set the function handle manually" fnAddress := (glx wglGetProcAddress: 'glResetHistogram'). fnAddress = 0 ifTrue:[^self error: 'Unable to locate glResetHistogram']. fnAddress := ExternalAddress new fromInteger: fnAddress. meth setHandle: fnAddress.
and then be invoked via
meth invokeWith: 16r8024 "GL_HISTOGRAM"
You'll have to do this for any extension you want to use and yes, it sucks big time.
<hack of the day>
I'll show you something that you may use AT YOUR OWN RISK WITH NO WARRANTY AND ABSOLUTELY NO RESPONSIBILITY ON MY PART. Don't ever try the following in any other system:
GLXWin32>>glResetHistogram: aTarget <apicall: ulong 'glResetHistogram' (ulong)> ^self extensionCallFailed: #glResetHistogram: withArguments:{aTarget}
GLXWin32>>extensionCallFailed: selector withArguments: args | ffiSpec fnName fnAddress | ffiSpec := thisContext sender method literalAt: 1. (ffiSpec isKindOf: ExternalLibraryFunction) ifFalse:[^self error: 'WHAT THE HELL ARE YOU TRYING???']. ffiSpec getHandle ifNotNil:[ ffiSpec getHandle asInteger = 0 ifFalse:[^self externalCallFailed]]. fnName := ffiSpec name. fnAddress := self wglGetProcAddress: fnName. fnAddress = 0 ifTrue:[^self error: 'Unable to locate ', fnName]. fnAddress := ExternalAddress new fromInteger: fnAddress. ffiSpec setHandle: fnAddress. ^self perform: selector withArguments: args
DANGERS: If your renderer changes this is likely to crash your system. I have only tried it once and it worked (and it should) but I cannot say what else may happen during a full session.
ADVANTAGES: You can use any extension you'd like as long as you follow the scheme used for glResetHistogram (e.g., declare the function with no "module:" and use #extensionCallFailed:withArguments: to reinvoke it). Also, except for the first-time invokation the above will be as efficient as any other ffi call.
</hack of the day>
Cheers, - Andreas
-----Original Message----- From: squeak-dev-admin@lists.squeakfoundation.org [mailto:squeak-dev-admin@lists.squeakfoundation.org] On Behalf Of Joshua 'Schwa' Gargus Sent: Saturday, December 21, 2002 10:09 PM To: Squeak Mailing List Subject: Croquet ARB_imaging
Hi,
I was wondering how to access the OpenGL ARB_imaging functions from withing Croquet. Whenever I try, I get the error "Unable to find function address". I used glGetString(GL_EXTENSIONS) to verify that GL_ARB_imaging is supported. I thing that the problem is the opengl32.dll doesn't provide the appropriate hooks (stupid Microsoft), even though NVIDIA's driver implements the functionality. I tried substituting the library "nvoglnt.dll" for "opengl32.dll", because the "strings" command told me that glMinMax appeared in the former file, which is part of the NVIDIA driver. This didn't work either, which isn't surprising, since it was basically a guess.
Other ideas? Joshua
Hi Andreas,
Thanks for the interesting hack. I have a question about it that I'll get to in a second.
Unfortunately, I wasn't able to try it because wglGetProcAddress: (as you defined it below) always returns 0, even when I try it on functions that I know are there, such as glColor3f(). I don't understand why this would be; 'Smalltalk listLoadedModules' includes opengl32.dll.
About your hack... when you say "if your renderer changes", you mean like if I have a dual-head setup with two different graphics cards, and I drag the Squeak window from one to the other? Wouldn't this be a problem even if the function could be found directly in opengl32.dll? Oh, I think I see... the function in opengl32.dll probably redirects the call to whichever driver is currently active. But when you use wglGetProcAddress, that indirection no longer occurs, so if the renderer changes, you're sunk. Does that sound right?
Thanks to you and Bert, Joshua
Joshua,
Unfortunately, I wasn't able to try it because wglGetProcAddress: (as you defined it below) always returns 0, even when I try it on functions that I know are there, such as glColor3f(). I don't understand why this would be; 'Smalltalk listLoadedModules' includes opengl32.dll.
wglGetProcAddress _exclusively_ works for extension functions. Trying it on any of the "standard" OpenGL functions will not work because they use the "standard" OS lookup mechanisms (and yes this all sucks but I didn't invent any of it).
In short, if the function you are using is part of the OpenGL standard (modulo versions of course) then you can and must use a "regular" ffi call. If it's not (e.g., part of an extension) then you must use wglGetProcAddress to use it. For some functions there are actually two variants (due to an extension becoming part of the standard) - for example I think you can still use glBindTextureEXT (used to be part of the texture object extension) as well as glBindTexture (it became part of OGL 1.1).
Generally, extensions have those "EXT", "ARB", "NV", "SGI", "ATI", "APPLE" (or whatever) postfixes which tell you that they're extensions. So the rule of thumb is: if it has a postfix, it's an extension if it doesn't it's part of the standard. (unfortunately this rule is not _always_ true such as in ARB_imaging)
I know it's confusing.
About your hack... when you say "if your renderer changes", you mean like if I have a dual-head setup with two different graphics cards, and I drag the Squeak window from one to the other? Wouldn't this be a problem even if the function could be found directly in opengl32.dll? Oh, I think I see... the function in opengl32.dll probably redirects the call to whichever driver is currently active. But when you use wglGetProcAddress, that indirection no longer occurs, so if the renderer changes, you're sunk. Does that sound right?
That sounds right but I cannot say if there aren't any other cases in which the "renderer changes". I would suspect that if it for example switches to software rendering for any reason you might crash too. In short, if the renderer is destroyed and then recreated you have a chance that it'll crash. Oh, and if you use *TWO* GLX'es which are differently allocated (e.g., on two screens or one hardware and one software) I would almost swear that it'll go nuts...
Cheers, - Andreas
On Sun, Dec 22, 2002 at 01:26:33AM +0100, Andreas Raab wrote:
Joshua,
Unfortunately, I wasn't able to try it because wglGetProcAddress: (as you defined it below) always returns 0, even when I try it on functions that I know are there, such as glColor3f(). I don't understand why this would be; 'Smalltalk listLoadedModules' includes opengl32.dll.
wglGetProcAddress _exclusively_ works for extension functions. Trying it on any of the "standard" OpenGL functions will not work because they use the "standard" OS lookup mechanisms (and yes this all sucks but I didn't invent any of it).
Good thing you mentioned that, because I was getting set to blame you :-)
In short, if the function you are using is part of the OpenGL standard (modulo versions of course) then you can and must use a "regular" ffi call. If it's not (e.g., part of an extension) then you must use wglGetProcAddress to use it. For some functions there are actually two variants (due to an extension becoming part of the standard) - for example I think you can still use glBindTextureEXT (used to be part of the texture object extension) as well as glBindTexture (it became part of OGL 1.1).
Generally, extensions have those "EXT", "ARB", "NV", "SGI", "ATI", "APPLE" (or whatever) postfixes which tell you that they're extensions. So the rule of thumb is: if it has a postfix, it's an extension if it doesn't it's part of the standard. (unfortunately this rule is not _always_ true such as in ARB_imaging)
So, are the functions in the imaging subset technically extensions? I saw a newsgroup post that said that they're not; they're full-fledged members of OpenGL 1.2, but are optional. And I *always* believe what I read on the net.
Anyway, I also get a 0 result when asking for the address of glMinmax, even though glGetString(GL_EXTENSIONS) says that the imaging subset is supported. I'll try with something that's a real extension (maybe NV_VERTEX_PROGRAM) after I finish eating a mom-cooked supper. I'll see what happens if I try it with a C++ program, too.
I'll keep you posted, Joshua
Joshua,
So, are the functions in the imaging subset technically extensions? I saw a newsgroup post that said that they're not; they're full-fledged members of OpenGL 1.2, but are optional.
This is true. But this entire issue is _very_ confusing due to Microsoft's reluctance to publicly support later OpenGL versions. Technically, OpenGL on Windows is stuck at 1.1. The hardware vendors support later OpenGL versions just fine but I _think_ OpenGL32.dll only exports the set of functions for 1.1. So I _think_ that even if the drivers support later versions they have no way of handing you the functions UNLESS you look them up through wglGetProcAddress - again I _think_ they are using this as a bypass method to give you access to later versions even if MS' opengl32.dll doesn't export it. I don't have anything definitive here - I always wanted to ask NVidia or ATI about this.
And I *always* believe what I read on the net.
You do? Oh well, did you know that every member of the Squeak mailing list has to send me a bottle of the best red wine that his region of the world can produce? Seriously! It's true! Eagerly waiting for your reply...
Anyway, I also get a 0 result when asking for the address of glMinmax, even though glGetString(GL_EXTENSIONS) says that the imaging subset is supported. I'll try with something that's a real extension (maybe NV_VERTEX_PROGRAM) after I finish eating a mom-cooked supper. I'll see what happens if I try it with a C++ program, too.
Yup, try to get a C program to do something interesting first - it's hard to guess these things if you're dealing with too many variables.
Cheers, - Andreas
Am Sonntag, 22.12.02 um 00:51 Uhr schrieb Joshua 'Schwa' Gargus:
About your hack... when you say "if your renderer changes", you mean like if I have a dual-head setup with two different graphics cards, and I drag the Squeak window from one to the other? Wouldn't this be a problem even if the function could be found directly in opengl32.dll? Oh, I think I see... the function in opengl32.dll probably redirects the call to whichever driver is currently active. But when you use wglGetProcAddress, that indirection no longer occurs, so if the renderer changes, you're sunk. Does that sound right?
Not quite. The function address returned by getProcAddress is OpenGL context specific. So when you have more than one context, or you destroy and create one, the absolutely correct way is to get a new function address. In practice, the address is only driver specific and can be safely shared across all contexts on a device. It may differ between physical screens, however.
-- Bert
Josh,
I finally got around to implement the "correct" solution for your problem. I'm attaching a version which should work well with the base Croquet version (if not, then check the iVars in GLX which is the only problematic place). To use it, check out class GLExtManager and in particular its *CLASS* side, for reasons following.
GLExtManager classComment<printIt>
GLExtManager handles OpenGL extension functions for GLX. Since extensions are specific for particular renderer/drivers all extensions must be looked up dynamically. I provide the technical means to handle extensions transparently for any number of contexts.
Declaring Extensions: ======================== Extension functions (and constants) have to be specifically declared. To declare functions and constants for an extension you need to do the following:
#1: Go to the CLASS side of GLExtManager and add a category that has the same name as listed in glGet(GL_EXTENSIONS). For example, to use the ARB imaging extension this category must be named GL_ARB_imaging (even though the extension is referred to as 'ARB imaging' glGet will tell us that the name GL_ARB_imaging so that is what you need to use). WARNING: The name must match EXACTLY, no extra spaces, watch for small and capital letters etc.
#2: Add a method which initializes the constants in this extension. The method itself MUST follow the convention to begin with 'initialize' and should then use the extension name. E.g., for initializing the constants in the ARB_imaging it should be called 'initializeArbImaging'.
The constants itself can be initialized by just copying them from the spec describing them and use the provided utility methods for initialization (just look over a few existing extensions). I am trying to make it easy for you to just copy those constants.
Note that all constants appear ONLY in the GLExtConstants - OpenGLConstants is exclusively used for standard OpenGL constants.
#3: Add the functions the extension defines. Generally, these should just be plain ffi call methods but there are three important issues: a) NEVER provide a 'module' for these functions. Since they are looked up by opengl specific means other ways are used and you MUST NOT provide a module. The extension this particular function is contained in is defined by the category and not by the module (this is to prevent confusion about 'I have no GL_ARB_imaging.dll' or even worse the possibility that on some system any such thing even exists (!)). b) NEVER do anything but just the plain FFI call (optionally followed by a plain return or call to #externalCallFailed). The method you are writing will actually be run in an entirely different place - you are only providing a template for the sake of your convenience (and speed). If you need more sophisticated error handling do this in the place where you call the method or provide a helper in GLX or something similar, but NEVER EVER DO THIS HERE. c) The calling convention is effectively ignored. Since it is platform specific it will in fact be replaced by the appropriate OS calling convention when used.
#4: Evaluate 'GLExtManager initialize' which will add the constants to GLExtConstants.
Using Extensions: ==================== Once you declared the extension, using it is simple. Since the constants defined by the extension are accessible through the GLExtConstants pool, you can just refer to them by name.
To invoke an extension method you need to query for the GLX' extension manager called 'glExt'. The extension manager is the guy who can understand the extension functions you declared. To give an example, in order to invoke the 'glUnlockArraysEXT' function you would use something like: glx glExt glUnlockArraysEXT etc.
IMPORTANT: You must NEVER, EVER keep a reference to the extension manager. All invokations have to be done transparently through glx.
In case you wonder why you cannot use glx directly, read the implementation details (the short answer is that there are Good Reasons (tm) not to do it).
Implementation details: =========================== In order to implement the dynamic lookup mechanism in the most convenient way, the GLExtManager is always created as a 'unique subclass' of GLExtManager. That is when you ask for a new extension manager using 'GLExtManager new' you actually get a subclass of GLExtManager which is denoted by an asterisk in front of it so it looks as *GLExtManager.
The *GLExtManager does not understand any of the function you have provided. When it runs into a message which it doesn't understand it performs the following functions: 1: First it looks if that method is in fact an extension method you defined. 2: If it is, it looks if the extension this method belongs to is present in the renderer it is bound to (e.g., the GLExtManager's glx inst var) 3: If the extension is present it looks at the functions you declared for this extension, and for each function - it copies the template method - it looks up calling convention / address of the function - it installs a new ffi call spec for that method - it adds this method to *GLExtManager 4: Once all the function for the extension have been loaded it reinvokes the message which failed.
Preloading extensions: ========================= As you can see from the implementation details, there is a certain overhead involved in handling extensions. In particular, there can be a noticable speed impact when an extension is loaded 'on demand' (e.g., when a message is not understood). To compensate for this, extensions can be loaded explicitly, by using, e.g., glx glExt loadExtension: #'GL_ARB_imaging'.
BTW, if anyone needs particular extensions it would be nice if you'd add them "to the pool" so that we can all easily use them (I hate typing all this stuff).
Cheers, - Andreas
Thanks a bunch Andreas! (I'll be on the lookout for a good bottle of wine)
I'm just about to go to bed before flying back to Atlanta, but I'll play with it soon.
Joshua
squeak-dev@lists.squeakfoundation.org