This post was updated on .
Hi all,
I'm currently working on a project to develop a relatively small framework with the goal of supporting game development efforts for students in a course. The scope of this project is an OpenGL-based renderer and a 3D scene graph, and must be written in Java. (That's the course pre-req. for students.) I've been using JOGL for a while and have also played around a bit with LWJGW3 --yes, I dare disturb you by mentioning a competitor :D-- There're some things I'd like to do, but I'm finding the requirement to have a GLAutoDrawable object a bit of a hassle. For example, I want to hide the library dependency from clients (i.e. clients use my interface, not JOGL directly). However, if/when needs to use a shader object (one I make available, not JOGL's ShaderProgram class), they'd need to be able to have a reference to the gl object for it to work, leaking the fact that I'm using JOGL through the interface. More concretely, code that looks like this example below: program.<Float> getInput("position_x").set(0.5f); would have to look, say, like this: GL4 gl = ... // get an drawable.getGL(), etc... program.<Float> getInput("position_x").set(gl, 0.5f); ...which is problematic; I want to avoid leaking JOGL 'knowledge' to the client program. For comparison, the static nature of LWJGL3 allows me use OpenGL functions at any point, without the requirement for an object instance like the GLAutoDrawable or implementing the GLEventListener interface --though it introduces other problems by having its own window mgmt API with GLFW, which causes similar 'leakage' on the input/event management side of the equation (i.e. cannot implement interfaces like MouseListener, KeyListener, etc). Is there some way in which JOGL can be used to make OpenGL calls (e.g. glUseProgram(...)) statically without GLAutoDrawable instance inside void display(GLAutoDrawable), or equivalent? I want to be able to make OpenGL calls anytime/anywhere. I hope this made sense. You should try taking a look at this post where I went into a bit more detail in some areas, if you feel you need clarification. I'd appreciate concrete suggestions, especially if you can provide actual examples what would illustrate your point more clearly. Thanks in advance, -x PS: BTW I've thought about using LWJGL3 + JOGL simultaneously. Is this even a good idea, or just a bomb of problems waiting go off? For example, if a GLCanvas were to be added to a JFrame and then, instead of using autodrawables in the display(...) override, I end up using OpenGL calls from the LWJGL3 instead. Is this even possible in principle as long as the GLContext is current? |
So, you are writing a wrapper around a library and that library uses callbacks, then you ofc need to wrap the callbacks also. That means that your library needs to wrap the window/frame/whatever drawable. It should also wrap the input handling. Then the client code register with the your library to get your library to call the client when it is time to draw or get keyboard input et c. That callback from your library to the client will expose only your library classes, these classes in turn can be wrappers around whatever native-bridge library you use.
You can look at the source code for the OSS projects like: LibGDX, Ardour3D, jMonkeyEngine to see how they do it. All of them hide the JOGL implementation by having their own 'render' interface. |
In reply to this post by xghost
There is good reasons behind why the JOGL API is NOT static:
Why does JOGL use Instances of GLContext / GL* instead of exposing a Static API? http://forum.jogamp.org/Why-does-JOGL-use-Instances-of-GLContext-GL-instead-of-exposing-a-Static-API-td4034144.html |
In reply to this post by jmaasing
Of course I could spend a significant amount of effort wrapping everything... but I think that seems to miss the point of, or not address, what I'm asking. I've checked some, not all, libraries (e.g. a bit of libGDX, a lot of Ogre3D) as I've been working on some things, but I don't think it's immediately practical for me to simply go around chasing every possible library someone can name in a "Check lib ____" comment :) I'll take a brief look at some of those tomorrow to see if I find something relevant to my question in the meantime, but if you'd like me to clarify something in my original post here or the link provided, please let me know. I think you may've misunderstood my post. I'm not questioning design decisions in JOGL or anything like that, so a post on why JOGL was designed like this, although interesting, is not too helpful at this point for me. Please let me know if there's something I should clarify in my original post, etc. Thanks again, -x |
Administrator
|
In reply to this post by xghost
On 07/23/2015 07:50 AM, xghost [via jogamp] wrote:
> Hi all, > > I'm currently working on a project to develop a relatively small > framework with the goal of supporting game development efforts for > students in a course. The scope of this project is an OpenGL-based > renderer and a 3D scene graph, and must be written in Java. (That's > the course pre-req. for students.) > > I've been using JOGL for a while and have also played around a bit > with LWJGW3 --yes, I dare disturb you by mentioning a competitor > :D-- > > There're some things I'd like to do, but I'm finding the requirement > to have a GLAutoDrawable object a bit of a hassle. For example, I > want to hide the library dependency from clients (i.e. clients use my > interface, not JOGL directly). However, if/when needs to use a shader > object (one I make available, not JOGL's ShaderProgram class), they'd > need to be able to have a reference to the gl object for it to work, > leaking the fact that I'm using JOGL through the interface. > > More concretely, code that looks like this example below: > > program.<Float> getInput("position_x").set(0.5f); > > would have to look, say, like this: > > GL4 gl = ... // get an drawable.getGL(), etc... program.<Float> > getInput("position_x").set(gl, 0.5f); > > ...which is problematic; I want to avoid leaking JOGL 'knowledge' to > the client program. dynamic instance model <http://forum.jogamp.org/Why-does-JOGL-use-Instances-of-GLContext-GL-instead-of-exposing-a-Static-API-td4034144.html>. You can do at least two things. [1] Hiding JOGL details by wrapping the GL instance in an implementation specific class, which implements a public interface. This way, your users don't know the details .. but your implementation has access to your inner states, at least JOGL's GL instance. public interface APIContext {}; /** pp */ class APIState implements APIContext { final GL gl; ... } Or .. not recommended: [2] In case your API follows some sort of state constraints: - makeCurrent - user stuff w/ public API - release You could use thread local storage, i.e. ThreadLocal<> ~Sven signature.asc (828 bytes) Download Attachment |
Hi Sven, I agree. That's what I was doing, but in a separate RenderSystem class, which would internally implement the GLEventListener and simply expose a public method that would, in turn, cause the GLCanvas' display() method to be invoked (e.g. OpenGL4RenderSystem implements RenderSystem, GLEventListener // as a package private, only RenderSystem public, create impl using factory/manager). That part always seemed straightforward, though I may not have explained myself as well at first. The part that I feel I've not found straightforward to address is that I do want the client/user to work with shaders directly, as shown in my code snippet at the bottom (part of a working test class). That snippet is not using JOGL, but something I like is that the user/client can work with the shader object, 'declare' shader inputs, build the shader, set values for inputs, and so on, while keeping the public interface fairly simple and binding-agnostic. This is what I'd expect the client to work with directly, even after the GL instance is hidden away in a renderer class that implements the GLEventListener, etc. But notice that when the client asks the program to 'build()', if we expect that to happen immediately, instead of say, get queued as some sort of command (a complication I'd like to avoid), then it seems the GL instance would have to be exposed. I might be missing something, but it seems even the JOGL ShaderProgram class does this --although that's not a problem for JOGL itself. After the client has provided the shader, it would simply send the 'current' shader program to the (not-yet-implemented) renderer class that I expect to work on in the near future. There're a lot of things I like about JOGL, particularly the fact that it integrates nicely with current Java components and stuff, and that was what I first tried using before I tried playing/learning LWJGL3 for the sake of a comparison. I liked that LWJGL3 allowed me to create the public interface I wanted in my GpuShaderProgram interface, but that's about it from what I've experienced. I'm trying to find a way to basically keep the same interface as shown below, but using JOGL instead. I hope I'm making sense. Thanks again, -x private void setupShader() { shaderMgr = new GpuShaderProgramManager(); GpuShaderProgram program = shaderMgr.createShaderProgram(PROGRAM_NAME, GpuShaderProgram.Type.GLSL); GpuShaderSubProgram vertexShader = program.createSubProgram(VERTEX_NAME, Stage.VERTEX_SHADER, getSourceVS()); GpuShaderSubProgram fragShader = program.createSubProgram(FRAGMENT_NAME, Stage.FRAGMENT_SHADER, getSourceFS()); program.add(vertexShader); program.add(fragShader); program.build(); program.add(program.createFloatInput("position_x", GpuShaderProgram.Input.Type.ATTRIBUTE)); program.add(program.createFloatInput("position_y", GpuShaderProgram.Input.Type.UNIFORM)); program.add(program.createVec4Input("position_v", GpuShaderProgram.Input.Type.ATTRIBUTE)); program.add(program.createVec4Input("frag_color", GpuShaderProgram.Input.Type.UNIFORM)); // ... } public void run() { while (...) { // ... GpuShaderProgram program = shaderMgr.getShaderProgram(PROGRAM_NAME); program.attach(); program.<Float> getInput("position_x").set((float) (Math.sin(System.currentTimeMillis() / 1000.0) * 0.5)); program.<Float> getInput("position_y").set((float) (Math.cos(System.currentTimeMillis() / 1000.0) * 0.5)); program.<Vec4> getInput("position_v").set(Vec4f.createFrom(0f, 0f, 0f, 1f)); program.<Vec4> getInput("frag_color").set(Vec4f.createFrom(1f, 1f, 1f, 1f)); glDrawArrays(GL_POINTS, 0, 1); program.detach(); } } |
Administrator
|
On 07/25/2015 03:25 AM, xghost [via jogamp] wrote:
> Sven Gothel wrote > You can do at least two things. > > [1] Hiding JOGL details by wrapping the GL instance in an > implementation specific class, which implements a public interface. > This way, your users don't know the details .. but your implementation > has access to your inner states, at least JOGL's GL instance. > > Hi Sven, > > I agree. That's what I was doing, but in a separate RenderSystem class, which > would internally implement the GLEventListener and simply expose a public > method that would, in turn, cause the GLCanvas' display() method to be invoked > (e.g. OpenGL4RenderSystem implements RenderSystem, GLEventListener // as a > package private, only RenderSystem public, create impl using factory/manager). > That part always seemed straightforward, though I may not have explained > myself as well at first. paradigm is optional, even though it is recommended. One can always use plain GLDrawable/GLContext etc. and handle the details manually. > > The part that I feel I've not found straightforward to address is that I > /do/ want the client/user to work with shaders directly, as shown in my code > snippet at the bottom (part of a working test class). That snippet is not > using JOGL, but something I like is that the user/client can work with the > shader object, 'declare' shader inputs, build the shader, set values for > inputs, and so on, while keeping the public interface fairly simple and > binding-agnostic. This is what I'd expect the client to work with directly, > even after the GL instance is hidden away in a renderer class that implements > the GLEventListener, etc. > > But notice that when the client asks the program to 'build()', if we expect > that to happen immediately, instead of say, get queued as some sort of command > (a complication I'd like to avoid), then it seems the GL instance would have > to be exposed. I might be missing something, but it seems even the JOGL > ShaderProgram class does this --although that's not a problem for JOGL itself. and _can_ be instantiated w/o a GL instance. Yes, ShaderCode's factory create(..) methods require a GL instance (for now), which is used to query GLSL capabilities. This can be avoided by using the public ShaderCode constructor and optionally the readShaderSource(..) method. We might want to change some of ShaderCode's API, e.g. drop the GL instance. This can only happen for our next minor release 2.4.*, where API changes are allowed. The ShaderCode and ShaderProgram does require the GL instance all of its methods performing OpenGL operations, ofc. I hope this helps a bit. ~Sven signature.asc (828 bytes) Download Attachment |
Hi Sven, Can you elaborate on this a bit? I'm not sure I'm familiar with this way of using JOGL. Does this mean that I can do something along the following lines without needing to be inside one of the GLEventListener overrides or something?: private void someMethodThatIsNotGLEventListenerDisplayOverride() { GL4 gl = GLContext.getGL().getGL4(); gl.glUseProgram(...); // ... more arbitrary OpenGL calls, etc... } I mean, if it's something that would allow me to request and get a GL instance at arbitrary locations, then it seems to be what I'm looking for --at least if I'm understanding this correctly. The ShaderProgram class has the link(...) method, which requires a GL instance to be passed in. This is what I had in mind, but yes the thing is that when the class needs to perform OpenGL calls internally, it needs the GL instance, which is what I was trying to avoid in the public interface of my shader classes. For example, when I instantiate one of my attributes (e.g. below), OpenGL calls need to be issued (at least with the current design): program.createFloatInput("position_x", GpuShaderProgram.Input.Type.ATTRIBUTE); It'll be expected to hold its own valid attribute ID. Since I already get the name, and the shader program itself is built, then this will (internally) result in an OpenGL call to glGetAttribLocation: public Input<Float> createFloatInput(String name, Input.Type type) { switch (type) { case ATTRIBUTE: return new GLSLFloatProgramAttribute1(this, name); // ... } } // .... GLSLFloatProgramAttribute1(GpuShaderProgram shader, String attributeName) { // ... location = glGetAttribLocation(shader.getId(), attributeName); // ... } Normally, for this to happen a valid/non-cached GL instance would have to be made available somewhere here (e.g. publicly when the client wants to call the createFloatInput method above to allow the gl.glGetAttribLocation(...) call). Something similar would be true when setting a value for the program input. For example, the line below would be expected to send the value for the GLSL vec4(1, 1, 1, 1) to the vertex program uniform, which would require an OpenGL call to glUniform4f in this case: program.createVec4Input("frag_color", GpuShaderProgram.Input.Type.UNIFORM); // ... program.<Vec4> getInput("frag_color").set(Vec4f.createFrom(1f, 1f, 1f, 1f)); // ... @Override public void set(Vec4 v) { // setting the frag_color uniform input :) glUniform4f(location, v.getX(), v.getY(), v.getZ(), v.getW()); } But for that, it'd also need a GL instance available. Perhaps (fingers crossed) your previous suggestion to do things manually might allow me to accomplish/reach the intended goal. That being said, I'm also open to updating my code ofc. Yes, and this is the part that I'm trying to allow, but without placing a GL parameter in the public interface as I wrap the JOGL binding dependency inside of my own. It does and I appreciate everyone's time, involvement, and help in this. Thanks again, -x |
The following seems to be possible, at arbitrary locations and without a need to use GLEventListener: GL4 gl = com.jogamp.opengl.GLContext.getCurrentGL().getGL4(); gl.glUseProgram(0); // ... Please let me know if I'm running into a time-bomb waiting to go off or if this is just fine. If this is a good way to use JOGL, then it seems it might allow me to do exactly what I needed. I'll wait for advice on this kind of use. Thanks in advance, -x |
Administrator
|
In reply to this post by xghost
On 07/25/2015 11:35 PM, xghost [via jogamp] wrote:
> Sven Gothel wrote > OK. > Just for the record, the GLAutoDrawable/GLEventListener 'assisted rendering' > paradigm is optional, even though it is recommended. > One can always use plain GLDrawable/GLContext etc. and handle the details > manually. > > Hi Sven, > > Can you elaborate on this a bit? I'm not sure I'm familiar with this way of > using JOGL. Does this mean that I can do something along the following lines > without needing to be inside one of the GLEventListener overrides or something?: the GLAutoDrawable/GLEventListener 'assisted' API model, but can also use the GLDrawableFactory to create a GLDrawable instance and create the GLContext manually. I would not recommend this path, since it requires detail knowledge about the OpenGL and windowing system lifecycle. All of the latter is handled when using the 'assisted' API model. This is sort of unrelated to your question, but I wanted to mention it. signature.asc (828 bytes) Download Attachment |
Administrator
|
In reply to this post by xghost
On 07/25/2015 11:41 PM, xghost [via jogamp] wrote:
> The following seems to be possible, at arbitrary locations and without a need > to use GLEventListener: > > GL4 gl = com.jogamp.opengl.GLContext.getCurrentGL().getGL4(); > gl.glUseProgram(0); > // ... > > > Please let me know if I'm running into a time-bomb waiting to go off or if > this is just fine. If this is a good way to use JOGL, then it seems it might > allow me to do exactly what I needed. is current. Otherwise a GLException is thrown or you receive the wrong GLContext (if many different are in use). Note: GLContext.getCurrent() will return null if none is current. In short: Yes it can work, given above constraints. However, it is still not the desired safe and recommended way to handle to handle this task. - GLContext.getCurrent() uses ThreadLocal<>, i.e. thread local storage (TLS), see <http://forum.jogamp.org/Why-does-JOGL-use-Instances-of-GLContext-GL-instead-of-exposing-a-Static-API-td4034144.html>. - Why querying an instance of a suppose to be known object within the workflow of a determined API/system? In the end .. it is your design decision, of course! Cheers, Sven signature.asc (828 bytes) Download Attachment |
Administrator
|
In reply to this post by Sven Gothel
It sums up pretty well the situation, thanks.
Julien Gouesse | Personal blog | Website
|
In reply to this post by Sven Gothel
That makes sense. Thanks for the clarification. Yes, the javadocs seemed to make this point, but It's good to see that I'm not about to cause a massive problem in the long-term, AFAIK. The application is expected to have a single window and OpenGL context, so I don't anticipate this becoming a problem. What would your recommendation be for the kind of task I'm trying to achieve? For example, one of my GLSL uniform classes looks as shown below. If you notice the 'set' method, it simply allows the client to call/set the value without making the GL instance visible. If there's a safer and better way to achieve this kind of goal, I'm open to it. (Yes, I already refactored the code to use JOGL and I have things working, which is why the code below is no longer showing the static call to LWJGL3.) class GLSLFloatProgramUniform1 extends GLSLProgramUniform<Float> implements GpuShaderProgram.Input<Float> { // ... @Override public void set(Float value) { GL4 gl = GLContext.getCurrentGL().getGL4(); gl.glUniform1f(location, value); } // ... } Read all of it, but I didn't find the question you seemed to want me to read about. Yup, just trying to make an informed decision so I won't regret it in hindsight :) Thanks again, -x |
Administrator
|
This post was updated on .
It might be a problem anyway and I don't see the benefit of not using our assisted API in a trivial case, I don't see any gain.
Julien Gouesse | Personal blog | Website
|
Hi gouessej, I'll try to simplify. 1. I have a requirement to decouple the client code using my library from the detail/fact that my library is (internally) using JOGL itself. 2. Using the assisted API (e.g. client code implementing GLEventListener) will cause clients to be 'aware' of JOGL's existence inside my library, breaking the JOGL encapsulation requirement (e.g. anything that makes GL instances directly visible/accessible to clients). 3. Client needs to work with shaders directly (e.g. set shader program input values, etc), hence my shader interfaces, as per the above code snippets. A bit of background: I've started a project that will be used in the long-term, but mostly for teaching purposes. It's not a trivial library, but it also won't be aiming to be a triple-A thing. We want to keep it "simple" enough to be understood within a single semester, but as complicated as needed to support students in game development projects, if that makes sense. We're in the process of replacing an older version of the same framework that has been in use for several years. Now, I hear there're problems, though I'm not sure what (specifically) those problems are supposed to be and how to avoid the pitfalls, other than what Sven and the javadocs already mentioned re GLContext. Could you please expand on these? What recommendation would you make that would help avoid the problems you're thinking about (would appreciate additional info on what they are) and also meet the basic requirements I summarized above? Please let me know if there's something you think I should clarify. Thanks in advance, -x |
Administrator
|
I'm not sure that using the assisted API forces you to expose anything that would be a concern for the encapsulation.
Sorry for the confusion. If you don't use the assisted API, you might have some problems anyway in some cases, for example when the OpenGL context must be created on a particular thread or when its creation is expected to fail several times on very poor drivers under Windows. Why not using the assisted API without exposing any GL instances directly? The desire of hiding JOGL sometimes leads to badly designed code. Moreover, if your students need to code in OpenGL, they will have to use JOGL. Look at how I "hide" JOGL most of the time in several major scenegraph API including JogAmp's Ardor3D Continuation and JMonkeyEngine.
Julien Gouesse | Personal blog | Website
|
Maybe for clarification, but what parts do you consider part of the 'assisted API'? Just want to make we're on the same page and that I'm not misunderstanding something. Are you thinking that I'm trying to create an OpenGL context 'manually'? I do intend to implement interfaces like GLEventListener (in a Renderer impl class), and this part is pretty easy to hide from clients. Pending clarification on 'assisted API', it seems this would be preferable. But not hiding details that are/should-be internal to my library (e.g. JOGL) would cause undesired coupling. I'm trying to avoid both :) They're not expected to work directly with OpenGL, only with a class to manage the scene and, at the lowest level, the renderer, but not directly with OpenGL API. (There's a separate course on graphics using OpenGL for that.) They're however, expected to interact with shaders in an abstracted way, which is where the classes I'm working on come in. I took a look at the JOGL renderers in both JME3 and Ardor3D frameworks. The JoglRenderer classes in both frameworks are doing exactly what I'm doing inside of my shader classes when I want to let users apply operations (e.g. set the value for a uniform in a shader) without exposing the JOGL and/or GL instances to them, i.e. using the GLContext to get the GL instance. This example, starting in line 128, does that when letting clients set the background color: // in com.ardor3d.renderer.jogl.JoglRenderer @Override public void setBackgroundColor(final ReadOnlyColorRGBA c) { final GL gl = GLContext.getCurrentGL(); _backgroundColor.set(c); gl.glClearColor(_backgroundColor.getRed(), _backgroundColor.getGreen(), _backgroundColor.getBlue(), _backgroundColor.getAlpha()); } Something similar happens in JME3's JoglRenderer, with its initialize() method is invoked (starting line 148): // in com.jme3.renderer.jogl.JoglRenderer public void initialize() { GL gl = GLContext.getCurrentGL(); // ... some comments .... if (gl.isExtensionAvailable("GL_VERSION_2_0")) { // .... For comparison, a relevant portion in one of my shader classes representing a Uniform input for a shader. Notice that the GL instance is not passed in, it's acquired just like in the previous snippets from Ardor3D/JME3: @Override public void set(Float value) { // ... GL4 gl = GLContext.getCurrentGL().getGL4(); gl.glUniform1f(location, value); } However, if I understand you correctly, using JOGL this way is what you're recommending against. Is this correct? I'm thinking that I may've misunderstood what you mean by 'assisted API' or something else, perhaps. Thanks again, -x |
Administrator
|
I was talking about JogAmp's Ardor3D Continuation, not about the old unmaintained Ardor3D, please look at the bottom of this page, there is a complete section about Ardor3D:
http://jogamp.org/wiki/index.php/Main_Page Please use an animator and at least one GLEventListener. Don't create your own context manually, use GLAutoDrawable, don't swap your buffers manually except if you have a good reason to do so and if you really know what you are doing.
Julien Gouesse | Personal blog | Website
|
Thanks for the clarification. Was not aware of the difference. I took a look (e.g. com.ardor3d.framework.jogl.JoglCanvas) and we seem to be doing similar things, so it seems ok. I intended to call the internal GLCanvas' display() method when it's the renderer's "turn" to process things (e.g. renderer.renderScene() ). If you think there's something that makes the Animator a requirement, please let me know. The docs don't seem to give that impression. Yup, already had the do's and don't's covered, and updates to the render target already take place by using the canvas' display method, which is what the Animator makes use of. Thanks for the help, -x |
Administrator
|
JogAmp's Ardor3D Continuation contains almost 90 commits ahead of the legacy Ardor3D, I don't advise you to look at com.ardor3d.framework.jogl.JoglCanvas as I hesitated to deprecate it.
Using a plain animator (Animator) would allow you to keep concentrated on the rest. An animator can use some code specific to AWT depending on the drawable you use, look at AWTAnimatorImpl. Therefore, I advise you to use a plain animator even for a very dumb example.
Julien Gouesse | Personal blog | Website
|
Free forum by Nabble | Edit this page |