weird rendering artifact

Started by raft, March 07, 2013, 11:44:20 PM

Previous topic - Next topic

raft

i've faced a weird rendering artifact which I couldnt still find a reason. please see the video below:

aptalkarga.com/tmp/skymaze_weird.avi

this only and only happens, when resolution is changed (FrameBuffer is recreated) during initial intro rotation and rotation is resumed afterwards. while capturing video somehow it ended prematurely but in real case always ends when rotation stops. does not happen if rotation is restarted or display changed after rotation. ends immediately if rotation is stopped manually.

seems like a multi threading issue but I see no reason for that. all gui code is rendered in GL thread.

really weird, any ideas?

EgonOlsen

That's strange...i don't see any relation between changing the framebuffer and the rotation. What rotates actually? The camera or the objects?

raft

camera. it moves in a circular path and looks at circle center

EgonOlsen

Actually, changing the framebuffer while the application is running isn't fully supported. When using compiled objects, there might be problems to recover from this. However, if you don't use compiled objects (or no VBOs/display lists), it should actually work fine and in my test case, it does. And in no case, it should give you this strange flickering...

I'll post my simple test case here. It uses a way to resize the FrameBuffer instead of destroying and recreating it. That's the better way because it preserves the context and avoids crashes when disposing a framebuffer that can occur on some NVidia cards under Windows.

Please tell me how it works:


import java.awt.Color;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

import com.threed.jpct.*;
import com.threed.jpct.util.Light;


public class SwitchTest
{
  private static final long serialVersionUID = 1L;
  private FrameBuffer fb = null;
  private World world = null;
  private Object3D plane = null;
  private Object3D ramp = null;
  private Object3D cube2 = null;
  private Object3D sphere = null;


  public SwitchTest()
  {
  //
  }


  private void initStuff()
  {
    fb = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
    world = new World();
    fb.disableRenderer(IRenderer.RENDERER_SOFTWARE);
    fb.enableRenderer(IRenderer.RENDERER_OPENGL);

    ramp = Primitives.getCube(20);
    ramp.setAdditionalColor(Color.RED);
    plane = Primitives.getPlane(20, 10);
    plane.setAdditionalColor(Color.GREEN);
    sphere = Primitives.getSphere(30);
    sphere.setAdditionalColor(Color.CYAN);
    sphere.translate(-50, 10, 50);
    cube2 = Primitives.getCube(20);
    cube2.setAdditionalColor(Color.ORANGE);
    cube2.translate(60, -20, 60);

    plane.rotateX((float) Math.PI / 2f);
    ramp.rotateX((float) Math.PI / 2f);

    plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
    ramp.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
    sphere.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
    cube2.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);

    world.addObject(plane);
    world.addObject(ramp);
    world.addObject(sphere);
    world.addObject(cube2);

    Light light = new Light(world);
    light.setPosition(new SimpleVector(0, -80, 0));
    light.setIntensity(255, 130, 140);
    light.setAttenuation(-1);

    plane.compile();
    ramp.compile();
    sphere.compile();
    cube2.compile();

    world.setAmbientLight(10, 10, 10);
    world.buildAllObjects();
  }


  private void doIt()
    throws Exception
  {
    Camera cam = world.getCamera();
    cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
    cam.moveCamera(Camera.CAMERA_MOVEUP, 160);
    cam.lookAt(plane.getTransformedCenter());

    long s = System.currentTimeMillis();
    boolean changed = false;

    while (!Display.isCloseRequested())
    {
      if (System.currentTimeMillis() - s >= 2000 && !changed)
      {
        fb.resize(800, 600);
        Display.setDisplayMode(findMode(800, 600, Config.glColorDepth, Config.glZBufferDepth));
        changed = true;
      }

      fb.clear(Color.BLUE);
      world.renderScene(fb);
      world.draw(fb);
      fb.update();
      fb.display(null);
      world.getCamera().rotateZ(0.01f);
      Thread.sleep(10);
    }
  }


  public static void main(String[] args)
    throws Exception
  {
    SwitchTest cd = new SwitchTest();
    cd.initStuff();
    cd.doIt();
  }


  private DisplayMode findMode(int x, int y, int cbpp, int zbppm)
    throws Exception
  {
    int mode = -1;
    DisplayMode[] modes = Display.getAvailableDisplayModes();

    String os = System.getProperty("os.name");
    if (os != null && os.toLowerCase().indexOf("linux") != -1)
    {
      if (cbpp == 32)
      {
        cbpp = 24;
        Logger.log("Running on Linux with a 32bit color setting...adjusting to 24bit instead!");
      }
    }

    for (int i = 0; i < modes.length; i++)
    {
      if (modes[i].getWidth() == x && modes[i].getHeight() == y && modes[i].getBitsPerPixel() == cbpp
          && (Config.glRefresh == 0 || modes[i].getFrequency() == Config.glRefresh || !Config.glFullscreen))
      {
        mode = i;
        break;
      }
    }
    if (mode == -1)
    {
      for (int i = 0; i < modes.length; i++)
      {
        if (modes[i].getWidth() == x && modes[i].getHeight() == y && modes[i].getBitsPerPixel() >= cbpp
            && (modes[i].getFrequency() >= Config.glRefresh || !Config.glFullscreen))
        {
          mode = i;
          break;
        }
      }
      if (mode == -1)
      {
        for (int i = 0; i < modes.length; i++)
        {
          if (modes[i].getWidth() == x && modes[i].getHeight() == y)
          {
            mode = i;
            break;
          }
        }
      }

      if (mode == -1)
      {
        return null;
      }
    }
    return modes[mode];
  }
}




raft

it works ok. no flickering at all. i tried same technique in my game but didnt help.

I've noticed someting which is not very noticable in the video. flickering happens only for SkyBox (my own skybox, not the jPCT's one). I've removed skybox rendering and flickering has gone. i've also tried removing FrameBuffer.clearZBufferOnly() call after skybox rendering but didnt help flickering.

btw, is it possible to go fullscreen without recreating FrameBuffer?

EgonOlsen

Quote from: raft on March 08, 2013, 02:50:21 PM
btw, is it possible to go fullscreen without recreating FrameBuffer?
Yes, you could do something like:


if (System.currentTimeMillis() - s >= 2000 && !changed)
      {
        fb.resize(800, 600);
        Config.glFullscreen = true;
        Display.setDisplayModeAndFullscreen(findMode(800, 600, Config.glColorDepth, Config.glZBufferDepth));
        changed = true;
      }


In Eclipse and with my test case, this somehow causes the null pointer in the awt event dispatch thread. No idea why, but it should be possible to avoid that (or ignore it...).

EgonOlsen

Quote from: raft on March 08, 2013, 02:50:21 PM
I've noticed someting which is not very noticable in the video. flickering happens only for SkyBox (my own skybox, not the jPCT's one). I've removed skybox rendering and flickering has gone. i've also tried removing FrameBuffer.clearZBufferOnly() call after skybox rendering but didnt help flickering.
Have you tried with jPCT's skybox implementation instead?

raft

Quote from: EgonOlsen on March 08, 2013, 02:57:03 PM
Have you tried with jPCT's skybox implementation instead?
yes, now tried, didnt help.

i've noticed this doesnt happen for all levels. seems as (but not 100% sure) happens when an excessive amount of particles used. like the levels with many lava tiles.

EgonOlsen


raft

sure, here it is:

btw, I set Config.glRefresh to 0, to let jPCT pick a mode

...
Loading Texture...from InputStream
Expanding buffers...200000 bytes
Loading Texture...from InputStream
...
Java version is: 1.6.0_13
-> support for BufferedImage
Version helper for 1.5+ initialized!
-> using BufferedImage
Software renderer (OpenGL mode) initialized
Software renderer disposed
Current mode:1024 x 768 x 32 @72Hz
Driver is: igdumdx32/8.15.10.1892 on ATI Technologies Inc. / ATI Mobility FireGL V5700
GL_ARB_texture_env_combine supported and used!
FBO supported and used!
VBO supported and used!
OpenGL renderer initialized (using 4 texture stages)
08.Mar.2013 17:30:20 raft.jumpy.gl.JumpyTWL loadLevel
INFO: loading level: 0
Software renderer disposed
08.Mar.2013 17:30:21 raft.jumpy.gl.JumpyTWL render
INFO: disposing game
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
08.Mar.2013 17:30:21 raft.jumpy.gl.JumpyTWL$4 run
INFO: level loaded: 0
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
No octree found in serialized data!
New WorldProcessor created using 1 thread(s) and granularity of 1!
Creating new world processor buffer for thread main
New WorldProcessor created using 1 thread(s) and granularity of 1!
Creating new world processor buffer for thread main
08.Mar.2013 17:30:28 raft.jumpy.gl.JumpyTWL applyOptions
INFO: changing videomode to Resolution: 960x600x32 -- ZBuffer depth: 24 @ 60Hz
recreateFrameBuffer
08.Mar.2013 17:30:28 raft.jumpy.gl.Options saveNow
INFO: saved options
OpenGL renderer disposed
Visibility lists disposed!
Visibility lists disposed!
Java version is: 1.6.0_13
-> support for BufferedImage
Version helper for 1.5+ initialized!
-> using BufferedImage
Software renderer (OpenGL mode) initialized
Software renderer disposed
Current mode:960 x 600 x 32 @60Hz
Driver is: igdumdx32/8.15.10.1892 on ATI Technologies Inc. / ATI Mobility FireGL V5700
GL_ARB_texture_env_combine supported and used!
FBO supported and used!
VBO supported and used!
OpenGL renderer initialized (using 4 texture stages)
Software renderer disposed
08.Mar.2013 17:30:30 raft.jumpy.gl.Options saveNow
INFO: saved options
New WorldProcessor created using 1 thread(s) and granularity of 1!
08.Mar.2013 17:30:37 raft.jumpy.gl.JumpyTWL render
INFO: disposing game
Creating new world processor buffer for thread main
New WorldProcessor created using 1 thread(s) and granularity of 1!
Creating new world processor buffer for thread main
OpenGL renderer disposed
Visibility lists disposed!
Visibility lists disposed!
Visibility lists disposed!


raft

btw, I guess this findMode code is used in engine, right? then I would suggest using

os.toLowerCase(Locale.ENGLISH).indexOf("linux")

instead of

os.toLowerCase().indexOf("linux")

since for example in turkish, LINUX's lowecase will be lınux, not linux

EgonOlsen

Quote from: raft on March 08, 2013, 04:51:07 PM
since for example in turkish, LINUX's lowecase will be lınux, not linux
Interesting... ;)...i'll change it.

About the actual problem: I've no idea. I extended the test case to feature a skybox and 4000 "particles", but i still can't reproduce the problem:


import java.awt.Color;
import java.util.Locale;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

import com.threed.jpct.*;
import com.threed.jpct.util.Light;
import com.threed.jpct.util.SkyBox;

public class SwitchTest {
private static final long serialVersionUID = 1L;

private FrameBuffer fb = null;

private World world = null;

private Object3D plane = null;

private Object3D ramp = null;

private Object3D cube2 = null;

private Object3D sphere = null;

private Object3D bb = null;

private Object3D[] planes = new Object3D[4000];

private SkyBox sky = null;

public SwitchTest() {
//
}

private void initStuff() {
fb = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
world = new World();
fb.disableRenderer(IRenderer.RENDERER_SOFTWARE);
fb.enableRenderer(IRenderer.RENDERER_OPENGL);

ramp = Primitives.getCube(20);
ramp.setAdditionalColor(Color.RED);
plane = Primitives.getPlane(20, 10);
plane.setAdditionalColor(Color.GREEN);
sphere = Primitives.getSphere(30);
sphere.setAdditionalColor(Color.CYAN);
sphere.translate(-50, 10, 50);
cube2 = Primitives.getCube(20);
cube2.setAdditionalColor(Color.ORANGE);
cube2.translate(60, -20, 60);

plane.rotateX((float) Math.PI / 2f);
ramp.rotateX((float) Math.PI / 2f);

plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
ramp.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
sphere.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
cube2.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);

world.addObject(plane);
world.addObject(ramp);
world.addObject(sphere);
world.addObject(cube2);

Light light = new Light(world);
light.setPosition(new SimpleVector(0, -80, 0));
light.setIntensity(255, 130, 140);
light.setAttenuation(-1);

plane.compile();
ramp.compile();
sphere.compile();
cube2.compile();

world.setAmbientLight(10, 10, 10);
world.buildAllObjects();

TextureManager tm = TextureManager.getInstance();
tm.addTexture("left", new Texture(64, 64, Color.RED));
tm.addTexture("front", new Texture(64, 64, Color.GRAY));
tm.addTexture("right", new Texture(64, 64, Color.GREEN));
tm.addTexture("back", new Texture(64, 64, Color.YELLOW));
tm.addTexture("up", new Texture(64, 64, Color.MAGENTA));
tm.addTexture("down", new Texture(64, 64, Color.ORANGE));

sky = new SkyBox(800);

bb = Primitives.getPlane(1, 2);
for (int i = 0; i < planes.length; i++) {
Object3D tmp = bb.cloneObject();
tmp.compile();
tmp.shareCompiledData(bb);
tmp.build();
tmp.addParent(bb);
tmp.setBillboarding(true);
tmp.setAdditionalColor((int) (255f * Math.random()), (int) (255f * Math.random()), (int) (255f * Math.random()));
world.addObject(tmp);
tmp.translate(-100f + 200f * (float) Math.random(), -100f * (float) Math.random(), -100f + 200f * (float) Math.random());
}

}

private void doIt() throws Exception {
Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 250);
cam.moveCamera(Camera.CAMERA_MOVEUP, 160);
cam.lookAt(plane.getTransformedCenter());

long s = System.currentTimeMillis();
boolean changed = false;

while (!Display.isCloseRequested()) {
if (System.currentTimeMillis() - s >= 2000 && !changed) {
fb.resize(800, 600);
Display.setDisplayMode(findMode(800, 600, Config.glColorDepth, Config.glZBufferDepth));
changed = true;
}

fb.clear(Color.BLUE);
sky.render(world, fb);
world.renderScene(fb);
world.draw(fb);
fb.update();
fb.display(null);
bb.rotateY(0.1f);
world.getCamera().rotateZ(0.01f);
}
}

public static void main(String[] args) throws Exception {
SwitchTest cd = new SwitchTest();
cd.initStuff();
cd.doIt();
}

private DisplayMode findMode(int x, int y, int cbpp, int zbppm) throws Exception {
int mode = -1;
DisplayMode[] modes = Display.getAvailableDisplayModes();

String os = System.getProperty("os.name");
if (os != null && os.toLowerCase(Locale.ENGLISH).indexOf("linux") != -1) {
if (cbpp == 32) {
cbpp = 24;
Logger.log("Running on Linux with a 32bit color setting...adjusting to 24bit instead!");
}
}

for (int i = 0; i < modes.length; i++) {
if (modes[i].getWidth() == x && modes[i].getHeight() == y && modes[i].getBitsPerPixel() == cbpp
&& (Config.glRefresh == 0 || modes[i].getFrequency() == Config.glRefresh || !Config.glFullscreen)) {
mode = i;
break;
}
}
if (mode == -1) {
for (int i = 0; i < modes.length; i++) {
if (modes[i].getWidth() == x && modes[i].getHeight() == y && modes[i].getBitsPerPixel() >= cbpp && (modes[i].getFrequency() >= Config.glRefresh || !Config.glFullscreen)) {
mode = i;
break;
}
}
if (mode == -1) {
for (int i = 0; i < modes.length; i++) {
if (modes[i].getWidth() == x && modes[i].getHeight() == y) {
mode = i;
break;
}
}
}

if (mode == -1) {
return null;
}
}
return modes[mode];
}
}



Can you reproduce this problem on another machine?

raft

I've fully implemented switching resolution and fullscreen mode without recreating the FrameBuffer. and the problem mostly gone, it's undeterministic now and happens seldomly :o (switching mode is much more smoother now, thanks for the tip ;))

the main difference from previous try is notifying TWL renderer that display size changed.

does it make any difference where Display is resized? I mean before calling FrameBuffer.display(..) or after or anywhere else?


EgonOlsen

Quote from: raft on March 08, 2013, 05:24:58 PM
does it make any difference where Display is resized? I mean before calling FrameBuffer.display(..) or after or anywhere else?
I'm not sure. I would place it at the beginning of the render loop.

raft

Quote from: EgonOlsen on March 08, 2013, 05:27:50 PM
I'm not sure. I would place it at the beginning of the render loop.
yes, I do the same..

I suspect this flickering happens due to some mismatch between jPCT and TWL settings.

below is what TWL does at start and end of each cycle. there is also clipping via GL11.glScissor if clipping is enabled for widgets.

    protected void setupGLState() {
        GL11.glPushAttrib(GL11.GL_ENABLE_BIT|GL11.GL_TRANSFORM_BIT|GL11.GL_HINT_BIT|
                GL11.GL_COLOR_BUFFER_BIT|GL11.GL_SCISSOR_BIT|GL11.GL_LINE_BIT|GL11.GL_TEXTURE_BIT);
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPushMatrix();
        GL11.glLoadIdentity();
        GL11.glOrtho(0, width, height, 0, -1.0, 1.0);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPushMatrix();
        GL11.glLoadIdentity();
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glEnable(GL11.GL_LINE_SMOOTH);
        GL11.glDisable(GL11.GL_DEPTH_TEST);
        GL11.glDisable(GL11.GL_LIGHTING);
        GL11.glDisable(GL11.GL_SCISSOR_TEST);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
    }
   
    protected void revertGLState() {
        GL11.glPopMatrix();
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPopMatrix();
        GL11.glPopAttrib();
    }