If the function pointer depends on the context, shouldn't it be 
stored/cached in the context, rather than globally?

On Thursday, 3 April 2014 11:23:30 UTC+2, Simon Danisch wrote:
>
> By the way, this solution doesn't deal with the problem, that the OpenGl 
> package needs to expose different sets of functions depending on the OpenGl 
> version, which can be only determined after the context creation. 
> Or is this bad style and one should just include all functions, leaving 
> unsupported function with C_NULL? But it would be a difference of a couple 
> of thousand lines of code.
>
> Its actually not trivial, as a package that doesn't create an opengl 
> context might include a particular version, independent of the version that 
> is actually used by the package, that deals with creating the opengl 
> context.
>
> Also, if we already start sacrificing performance, we could just go back 
> to the trivial solution, which is to put the getprocaddress into the ccall. 
> This at least guarantees, that one always gets the right pointer, even if 
> the context gets switched, destroyed or changed...
>
> On Apr 3, 2014 10:48 AM, "Mike Innes" <[email protected] <javascript:>> 
> 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] <javascript:>> 
> 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] <javascript:>> 
> 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]<javascript:>> 
> 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] <javascript:>> 
> 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]<javascript:>
> >
> >>> >> wrote:
> >>> >> > I stand corrected.
> >>> >> >
> >>> >> > On Wed, Apr 2, 2014 at 9:50 AM, Simon Danisch 
> >>> >> > <[email protected]<javascript:>
> >
> >>> >> > 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]<javascript:>
> >:
> >>> >> >>
> >>> >> >>> @ 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