(1) and (2) do equivalent operations at runtime, they only differ in
how the memoization is implemented (dict vs. global variable). Neither
requires an OpenGL context at compile time, making them roughly
equivalent

(3) caches information derived from the OpenGL context, which may be
different from the OpenGL context that is used at runtime.

An alternative approach is to mimic GLEW, as described by Isaiah, and
look them up in an initialization/loader routine.

Another alternative approach, especially useful if you want to allow
multiple contexts, is to apriori assign each function name an index
into a table of all functions and store this vector in the opengl
context. This is also exactly equivalent to (1), but using a perfect
hash function.

On Thu, Apr 3, 2014 at 4:48 AM, Mike Innes <[email protected]> wrote:
> Ok, that's interesting. This is obviously a pretty clever macro, because
> it's not at all obvious that it should run as quickly as it does.
>
> But, I'm still not sure I understand why that solution is necessarily the
> least brittle and most correct. As I see it, we have three potential
> solutions here, which in order of most elegant / least performant are (1)
> getFuncPointerM memoization, (2) @getFunctionPointer memoization, (3) @eval
> inlining. (1) and (2) are exactly the same amount of typing, (3) a few
> characters more. (3), I would argue, is more clear about what it does and
> why it's fast, but perhaps your opinion will differ.
>
> Without clear reasoning, drawing a line of "correctness" between (2) and (3)
> seems pretty arbitrary. I can see why you might argue that only (1) is
> correct, since it's evidently easier to understand and maintain. But what
> exactly makes (3) so much worse than (2), objectively speaking?
>
>
> On 3 April 2014 09:21, Simon Danisch <[email protected]> wrote:
>>
>> Can someone explain me what exactly is brittle about it?
>> I don't like the solution too much myself, as it disguises things a
>> little.
>>
>> But I don't know Julia ( or macros and programming concepts in general)
>> well enough to see the bad side effects.
>> At least it gets the job done, which is to generate a function body that
>> only consists of a ccall with an inlined function ptr.
>>
>> Or is this not desirable?
>>
>> On Apr 3, 2014 5:19 AM, "Jameson Nash" <[email protected]> wrote:
>>>
>>> As I indicated, I used the macro to make the code look neater. I could
>>> have expanded it (see below). But just as functions help DRY code,
>>> macros help DRY typing. Memoizing using a dict isn't much better than
>>> calling dlsym, which is also some form of dict (and may even benefit
>>> from better memory localization). I am doing almost the same (using
>>> the module global namespace as a dictionary), but I am cheat because I
>>> can inform the compiler that the result of this dictionary lookup is
>>> static, which lets it emit a direct entry into the module global
>>> lookup table, at essentially negligible runtime performance impact
>>> (one mov and one jmp instruction more than directly ccall'ing a
>>> pointer).
>>>
>>> Using eval to do inlining is a brittle usage of both eval and
>>> inlining. it is nice to have the computer run things fast, but it is
>>> better have it do them right :)
>>>
>>> const glGetString_func_pointer = C_NULL
>>> function glGetString(name::Uint16)
>>>     global glGetString_func_pointer
>>>     if glGetString_func_pointer::Ptr{Void} == C_NULL
>>>         glGetString_func_pointer::Ptr{Void} =
>>> getFuncPointer("glGetString")
>>>     end
>>>     ccall(glGetString_func_pointer::Ptr{Void}, Ptr{Cchar}, (Uint16,),
>>> name)
>>> end
>>>
>>> On Wed, Apr 2, 2014 at 9:59 PM, Mike Innes <[email protected]>
>>> wrote:
>>> > I agree entirely that macros and eval should be avoided if possible –
>>> > so why
>>> > have you used them for memoization?
>>> >
>>> >
>>> > const func_pointers = Dict{String, Ptr{Void}}()
>>> >
>>> >
>>> > getFuncPointerM(name) =
>>> >
>>> >   haskey(func_pointers, name) ?
>>> >
>>> >     func_pointers[name] :
>>> >
>>> >     (func_pointers[name] = getFuncPointer(name))
>>> >
>>> >
>>> > Perhaps I'm missing something, but that seems equivalent and a lot
>>> > neater to
>>> > me. The benefit to your macro is that it's faster, which is exactly the
>>> > reason to use eval in Simon's case.
>>> >
>>> > If retrieving a pointer carries a significant overhead compared to
>>> > inlining
>>> > it (which may well be a concern in an OpenGL library), then using eval
>>> > to do
>>> > that inlining is perfectly justified.
>>> >
>>> >
>>> > On 3 April 2014 02:13, Jameson Nash <[email protected]> wrote:
>>> >>
>>> >> A delayed macro is just a function call. Don't make you life
>>> >> complicated by trying to use one in place of the other.
>>> >>
>>> >> Using macros and eval can get you into a lot of trouble by helping you
>>> >> write brittle code. They allow you to confuse compile and run time,
>>> >> even though Julia does make a strong distinction between them. Avoid
>>> >> using them.
>>> >>
>>> >> The correct way to write this is using memoization. We can use a macro
>>> >> to make this look neater. This one is copied from PyCall:
>>> >>
>>> >> macro getFuncPointer(func)
>>> >>     z = gensym(string(func))
>>> >>     @eval global $z = C_NULL
>>> >>     quote begin
>>> >>         global $z
>>> >>         if $z::Ptr{Void} == C_NULL
>>> >>             $z::Ptr{Void} = $(getFuncPointer(esc(func))))
>>> >>         end
>>> >>         $z::Ptr{Void}
>>> >>     end end
>>> >> end
>>> >>
>>> >> glGetString(name::GLenum) = ccall(@getFuncPointer("glGetString"),
>>> >> ...., ...., name)
>>> >>
>>> >>
>>> >> A good macro should be equivalent to a pure function: regardless of
>>> >> when or how it is run, or how the program state changes, it must
>>> >> return the same result given the same inputs. In this case, it returns
>>> >> a static variable and a method of retrieving the actual function
>>> >> pointer. Note that it does not actually look up the function pointer
>>> >> or return it -- that cannot be done until runtime.
>>> >>
>>> >> eval is actually just a macro call in disguise, so the same guidelines
>>> >> apply. This also happens to demonstrate an appropriate (safe) use of
>>> >> eval.
>>> >>
>>> >>
>>> >> PS. this code should be causing a compile-time deprecation warning
>>> >> (because of the constant there), I'll have to investigate why that
>>> >> code is not working anymore:
>>> >>         return top(ccall)(Ptr{Void}
>>> >> @0x00007f402e6dadc0,Ptr{Int8},(Uint16,),name::Uint16,0)::Ptr{Int8}
>>> >>
>>> >> On Wed, Apr 2, 2014 at 11:56 AM, Isaiah Norton
>>> >> <[email protected]>
>>> >> wrote:
>>> >> > I stand corrected.
>>> >> >
>>> >> > On Wed, Apr 2, 2014 at 9:50 AM, Simon Danisch <[email protected]>
>>> >> > wrote:
>>> >> >>
>>> >> >> Ah, the links are all the same...
>>> >> >> Output for Mikes solution:
>>> >> >> native: https://gist.github.com/SimonDanisch/6270c01a6ea881877c4f
>>> >> >> llvm:   https://gist.github.com/SimonDanisch/612e8b08d915d188c4d5
>>> >> >>
>>> >> >>
>>> >> >>
>>> >> >>
>>> >> >> 2014-04-02 15:46 GMT+02:00 Simon Danisch <[email protected]>:
>>> >> >>
>>> >> >>> @ Mike
>>> >> >>> Good question. Well, I definitely would wish for a simpler
>>> >> >>> solutions.
>>> >> >>> But as far as I understand the situation, every video card vendor
>>> >> >>> makes
>>> >> >>> his own OpenGL implementation.
>>> >> >>> Also, even on one computer, you can have more than one rendering
>>> >> >>> context.
>>> >> >>> This means, the correct function pointers can be just queried,
>>> >> >>> after
>>> >> >>> one
>>> >> >>> creates a particular context.
>>> >> >>>
>>> >> >>>
>>> >> >>> So I looked into the machine and llvm code and its pretty clear!
>>> >> >>> The version inspired by Mike generates a lot shorter llvm and
>>> >> >>> machine
>>> >> >>> code.
>>> >> >>> It also doesn't matter if I call glGetString before inspecting the
>>> >> >>> machine code.
>>> >> >>>
>>> >> >>> My test program for Isaiah's solution:
>>> >> >>>
>>> >> >>> #glTest.jl
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> using GLUT, OpenGL
>>> >> >>>
>>> >> >>> glutInit()
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA |
>>> >> >>> GLUT_MULTISAMPLE | GLUT_ALPHA)
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> glutInitWindowPosition(0, 0)
>>> >> >>> glutInitWindowSize(1,1)
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> window = glutCreateWindow("dummy")
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> println(code_llvm(glGetString, (Uint16,)))
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> println(bytestring(convert(Ptr{Uint8}, glGetString(0x1F02))))
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> println(code_llvm(glGetString, (Uint16,)))
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> #OpenGL.jl
>>> >> >>> module OpenGL
>>> >> >>>
>>> >> >>> function getFuncPointer(name)
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>     ccall( (:glXGetProcAddress, "libGL"), Ptr{Void},
>>> >> >>> (Ptr{Cchar},),
>>> >> >>> name)
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> end
>>> >> >>> glGetString(name::Uint16) = ccall(getFuncPointer("glGetString"),
>>> >> >>> Ptr{Cchar}, (Uint16,), name)
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>>
>>> >> >>> export glGetString
>>> >> >>> end
>>> >> >>>
>>> >> >>>
>>> >> >>> Output:
>>> >> >>> https://gist.github.com/SimonDanisch/777bfc7783f9e96818f4
>>> >> >>>
>>> >> >>> Output for Mikes solution:
>>> >> >>> native: https://gist.github.com/SimonDanisch/777bfc7783f9e96818f4
>>> >> >>> llvm:    https://gist.github.com/SimonDanisch/777bfc7783f9e96818f4
>>> >> >>>
>>> >> >>>
>>> >> >>> Am I missing something?
>>> >> >>>
>>> >> >>>
>>> >> >>> Am Dienstag, 1. April 2014 14:30:12 UTC+2 schrieb Simon Danisch:
>>> >> >>>>
>>> >> >>>> Hi,
>>> >> >>>> I’m working on the OpenGL package and I want to make it finally
>>> >> >>>> usable
>>> >> >>>> in a nice and clean way on all platforms.
>>> >> >>>> The problem is, that one needs pointer for the GL functions,
>>> >> >>>> which
>>> >> >>>> you
>>> >> >>>> can only get, after initialization of the OpenGL context.
>>> >> >>>> But initializing the context and creating a window shouldn’t be
>>> >> >>>> part
>>> >> >>>> of
>>> >> >>>> the OpenGL package.
>>> >> >>>>
>>> >> >>>> So I tried two different approaches, which both seem to have
>>> >> >>>> their
>>> >> >>>> downsides:
>>> >> >>>>
>>> >> >>>> 1.
>>> >> >>>> Initialize OpenGL context when including the OpenGL package
>>> >> >>>> This is bad, because this makes the OpenGL package dependent on
>>> >> >>>> some
>>> >> >>>> third party OpenGL context creation library.
>>> >> >>>>
>>> >> >>>> 2.
>>> >> >>>> Load the functions later with a loading Function.
>>> >> >>>> Bad, because the function definitions are not visible for any
>>> >> >>>> other
>>> >> >>>> module, that relies on the OpenGL package.
>>> >> >>>>
>>> >> >>>> My ideal solution would be, to evaluate a macro when the function
>>> >> >>>> is
>>> >> >>>> called and not when the module is included.
>>> >> >>>> Like this, I can define all the OpenGL functions already in the
>>> >> >>>> OpenGL
>>> >> >>>> module, and when you call them the first time,
>>> >> >>>> the right function ptr gets inserted into the ccall, or an error
>>> >> >>>> is
>>> >> >>>> raised, when OpenGL context is not initialized.
>>> >> >>>>
>>> >> >>>> this could look like this:
>>> >> >>>>
>>> >> >>>> module OpenGL
>>> >> >>>>
>>> >> >>>> macro getFuncPointer(name::ASCIIString)
>>> >> >>>>    return getProcAddress(name)
>>> >> >>>> end
>>> >> >>>>
>>> >> >>>> glGetString(name::GLenum) = ccall(@getFuncPointer("glGetString"),
>>> >> >>>> ....,
>>> >> >>>> ...., name)
>>> >> >>>> export glGetString
>>> >> >>>> end
>>> >> >>>>
>>> >> >>>> using OpenGL
>>> >> >>>> ...create OpenGL context
>>> >> >>>> #define getProcAddress
>>> >> >>>> global const getProcAddress = glutGetProcAddress # If using GLUT
>>> >> >>>> for
>>> >> >>>> GL
>>> >> >>>> context creation
>>> >> >>>> #call gl Functions
>>> >> >>>> glGetString(GL_VERSION)
>>> >> >>>>
>>> >> >>>> Any ideas how to do this in a clean way?
>>> >> >>>>
>>> >> >>>>
>>> >> >>>> Cheers,
>>> >> >>>>
>>> >> >>>> Simon
>>> >> >>
>>> >> >>
>>> >> >
>>> >
>>> >
>
>

Reply via email to