Applet flickering with LWJGL

Started by ErDetEnAnd?, January 08, 2009, 10:51:42 PM

Previous topic - Next topic

ErDetEnAnd?

Hello again!

I'm trying to turn my application into an applet. In the appletviewer software and  hardware render (AWTGLCanvas) works fine. Same for software render when applet is loaded within a browser. The problem arises when using the AWTGLCanvas render inside the browser. What I get is a rendered image every second time, else it's a "near white" color - which is not the background from the rendered image.

The render code only runs on user/event input, thus it has no active render thread. Now, the render problem does not show here, but only on applet repaint eg when another window is dragged on top of the browser or the browser window is dragged from outside the screen and towards the screen center.

My applet painting methods look like this (like i've read http://www.jpct.net/forum2/index.php/topic,202.0.html):

    public void paint(Graphics g) {
        super.paint(g);
        if (worldFrame!=null)
            worldFrame.paint(g);
    }

    public void update(Graphics g) {
        paint(g);
    }


Where the worldframe is a JPanel which is the canvas is added to. The painting is done like this:

        if (buffer.usesRenderer(IRenderer.RENDERER_OPENGL))
            if (cGLScene!=null) {
                buffer.displayGLOnly();
                cGLScene.repaint();
            }


Does anyone have any idea?

EgonOlsen

Try to remove the super.paint(g); from paint()

ErDetEnAnd?

Removing that line is actually only makes the problem worse, like the image is only shown ca. each 3rd time.

According to the java applet doc, I'm advised to put in the super call at the top:

QuotePaints the container. This forwards the paint to any lightweight components that are children of this container. If this method is reimplemented, super.paint(g) should be called so that lightweight components are properly rendered. If a child component is entirely clipped by the current clipping setting in g, paint() will not be forwarded to that child.

EgonOlsen

The call to super.paint() should only be needed if you are actually using Swing/AWT components in that applet. However, maybe paul can help here...he did far more in respect to applets than i did.

paulscode

I was a little bit confused about the call to 'worldFrame.paint(g);'.  This looks like you are telling the JFrame to paint onto the JApplet's Graphics instance.  A JFrame will have its own Graphics instance, so if you want it to draw again, you must call repaint() instead of paint().

Personally, I have never tried adding a JFrame with a child Canvas as a child to a JApplet, since JFrame and JApplet are virtually identical as far as their methods go.  You can convert an application into an applet and vice versa by merely changing the word "JFrame" to "Japplet" and commenting out/uncommenting the constructor.  In theory, though, your way seems like it should work just fine, with the minor adjustment of changing that call to paint() into a call to repaint():

// JApplet paint() method:
public void paint( Graphics g )
{
    super.paint( g );  // don't know if this is needed, try it both ways
    if( worldFrame != null )
            worldFrame.repaint();
}


Let me know if this doesn't work.  If you simply can't get it to work, the other option (switching between JApplet and JFrame) is demonstrated in the updated source code for my Gears Demo Applet.  The comments indicate how to easily switch between JApplet and JFrame.

ErDetEnAnd?

Hi Paul. Thank you for your answer.

The missleading name worldFrame is actually a JPanel, but you're right about the graphic instance. The passed graphic instance is not used at all. So it looks like this:

    public void paint(Graphics g) {
        super.paint(g);
        if (worldFrame!=null)
            worldFrame.paint(null);
    }

    public void update(Graphics g) {
        paint(g);
    }


The reason why not calling repaint is that the scene should be drawn at the same time the applet is requested painted. By calling repaint to the JPanel I'm not having same control of when the scene is painted. Dont you agree?

Remember I'm only having this issue when the applet runs in a browser, with the gl canvas enabled.

Unfortunately the gears example is using active rendering.

paulscode

I agree it would be nice to be able to control exactly when something paints, but unfortunately you can not.  The best you can do is specify a "maximum number of miliseconds" until the paint() method will be called, using 'JPanel.repaint( long milis )'.  In case you were wondering, that is why your JPanel's paint() method calls 'worldFrame.repaint()' rather than 'worldFrame.paint( g )'

The reason you can't call a child component's paint( g ) directly from the parent's paint() method, is because what that does is pass the parent's Graphics instance to the child's paint() method so the child will paint onto the parent (which is hidden behind the child) instead of painting onto itself.  This will slow down your applet, since the JPanel's paint method is being called twice when there are "dirty regions" (once when AWT called the JApplet's paint() method and once when AWT called the JPanel's paint() method).  Not only that, you can't even save a handle to the different Graphics instance to use that instead, since it is different every time paint() gets called.

That being said, it should be pretty easy to change from active rendering into inactive.  I would use something like this (I'm not at a computer where I can test this at the moment, so let me know if it doesn't work):

First, create a method in your JApplet that gets called whenever something changes (you could simply use 'repaint()', unless there are things you want to do first).  If you need to call this method from your JPanel or Canvas classes (which you probably do), then create a global static handle to your JApplet, or pass a handle to it in the constructors for your JPanel and Canvas classes.
public void somethingChanged()
{
    // do whatever else you need to do here, then:
    this.repaint();
}


Second, your applet's paint() method, tell the JPanel to repaint().
public void paint( Graphics g )
{
    if( worldFrame != null )
            worldFrame.repaint();
}


Finally, your JPanel's paint() method, render the scene and tell the Canvas to repaint().
public void paint( Graphics g )
{
    if( buffer != null && world != null )
    {
        buffer.clear();
        world.renderScene( buffer );
        world.draw( buffer );
        buffer.update();
        if ( buffer.usesRenderer( IRenderer.RENDERER_OPENGL ) && cGLScene != null )
        {
            buffer.displayGLOnly();
            cGLScene.repaint();
        }
    }
}

paulscode

One more thing, if you absolutely need the scene to update at exactly the time the JApplet's paint() method is called (I can't see why this would be necessary, but in case it is), you could create a method in your JPanel that you call from the JApplet's paint() method.  It would update the scene and call repaint() on the JPanel:

// For the worldFrame JPanel:
public void updateScene()
{
    if( buffer != null && world != null )
    {
        buffer.clear();
        world.renderScene( buffer );
        world.draw( buffer );
        buffer.update();
        this.repaint();
    }
}
public void paint( Graphics g )
{
        if ( buffer != null && buffer.usesRenderer( IRenderer.RENDERER_OPENGL ) && cGLScene != null )
        {
            buffer.displayGLOnly();
            cGLScene.repaint();
        }
}

// For your JApplet:
public void paint( Graphics g )
{
    if( worldFrame != null )
    {
        worldFrame.updateScene();
    }
}


Only reason I would advise against this method if possible, is that paint() may be called before updateScene() gets called, so you'd probably also need to add some type of logic into your paint() method to make sure updateScene() has been called before.

ErDetEnAnd?

#8
Hi Paul, and once again thanks!

I've with interest read ur posts, and implemented what you suggested.

My applet:
    public void paint(Graphics g) {
        if (worldFrame!=null)
            worldFrame.repaint();
    }

    public void update(Graphics g) {
        paint(g);
    }


And the worldFrame which inherits from JPanel:

        MyAppletClass.my_static_applet_instance.repaint();

        if (buffer.usesRenderer(IRenderer.RENDERER_OPENGL))
            if (cGLScene!=null) {
                buffer.displayGLOnly();
                cGLScene.repaint();
            }


I've tried calling "MyAppletClass.my_static_applet_instance.repaint();" before and after I've requested a canvas repaint. But same result; flicker on repaint events caused by "dirty regions".

Futhermore I downloaded ur zipped gears example from above, and used primitives instead of 3DS (some how they were currupted). So changed 3 lines like so: redGear = Primitives.getBox(3,3);. This program is also causing flicker within the browser on "dirty region" update. Since you're rendering all the time, its just a matter of short time before the "near white" color, where the applet is running, is updated with a new image. But it is truely flickering as much as my applet.

Could we used ur program as reference?

Edit: I recall to having loaded your gears example from your webpage, but not the flickering. Do you still have it online with an appletloader?

ErDetEnAnd?

Okay found your online gears, but same problem :(

ErDetEnAnd?

Been testing a while now. Using worldFrame.repaint() in the paint method of the applet is not only causing flicker in the browser with GL enabled, but in all circumstances: software and hardware render + appletviewer and browser.

    public void paint(Graphics g) {
        if (worldFrame!=null)
            worldFrame.repaint();
    }

    public void update(Graphics g) {
        paint(g);
    }


Back to paint() the flicker only shows in browser using hardware render.

    public void paint(Graphics g) {
        if (worldFrame!=null)
            worldFrame.paint(null);
    }

    public void update(Graphics g) {
        paint(g);
    }

paulscode

Wow, thanks for the information, I didn't realize my applet was flickering in hardware rendering mode - perhaps my PC is too fast for me to notice.  I'll tinker around with this to see if I can solve the problem (it likely affects all my applets since they all use that same basic infrastructure used in the gears demo applet).  When my wife and I get back home on the 20th, I'll see if any of my test computers experience the problem.

Unfortunately, in the mean time, until I figure out what might be causing this, I can't really help you solve you're problem either.  Sorry to send you on a wild goose chase about paint() vs. repaint()! :-[

paulscode

I have some computers here where I am at (a couple of them are quite slow), but I was unable to see any flicker on my gears demo applet on any of the computers.

Anyone else reading this post, do you experience flicker in hardware mode for the gears demo?

Just to make sure we are on the same page, ErDetEnAnd are you experiencing flicker more-or-less every frame as the gears rotate in hardware rendering mode (in other words, do you start up the applet and it immediately begins flickering), or are you talking about something else?  How do you produce the problem?

ErDetEnAnd?

It only flickers when another window is dragged on top of the browser or the browser window is dragged from outside the screen and towards the screen center. So when the applet's paint method is called by the system.

paulscode

I see that now.  Never thought it was a problem though - how often is someone going to be dragging the window around while they are playing?

I wonder if this is a limitation of applets in general, or just my applet?  Looking at the code, I don't see any reason this would happen?