Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (497)
Games in Android Showcase (114)
games submitted by our members
Games in WIP (563)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Screen Tearing  (Read 2255 times)
0 Members and 1 Guest are viewing this topic.
Offline Mizzath

Senior Newbie





« Posted 2013-02-04 04:53:40 »

Hello all,

I seem to be experiencing a bit of screen tearing -- or, at least, that's what I hope it is.

How does one go about mending this heinous deed?



Level.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
package com.mizzath.sandbox.level;

import java.util.Random;

import com.mizzath.sandbox.screen.Screen;
import com.mizzath.sandbox.tile.Tile;

public class Level {
   public int width, height;
   public int xOffset, yOffset;
   public int[] tiles;
   public Screen screen;
   
   Random random = new Random();
   
   public Level(int width, int height, Screen screen) {
      this.width = width;
      this.height = height;
      this.screen = screen;
     
      tiles = new int[width * height];
     
      for (int i = 0; i < tiles.length; i++) {
         tiles[i] = random.nextInt(2);
      }
   }
   
   public void render(int xOffset, int yOffset) {  
      int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
     
      int xPixelChange = 0, yPixelChange = 0;
     
      if (yOffset > 0) {
         if (yOffset <= 16) {
            yPixelChange = -yOffset; // ypos + yPixelChange
           y0 = 0;
         } else if (yOffset > 16) {
            yPixelChange = -(yOffset % 16); // ypos + yPixelChange
           y0 = yOffset >> 4;
         }
      } else if (yOffset < 0) {
         if (yOffset >= -16) {
            yPixelChange = 16 + yOffset; // ypos - yPixelChange
           y0 = -1;
         } else if (yOffset < -16) {
            yPixelChange = (16 + (yOffset % 16)); // ypos - yPixelChange
           y0 = (yOffset - yPixelChange) >> 4;
         }
      }
     
      int deltaY = yOffset + screen.height;
      y1 = deltaY + (16 - (deltaY % 16)) >> 4;
     
      if (xOffset > 0) {
         if (xOffset <= 16) {
            xPixelChange = 16 - xOffset; // xpos - xPixelChange
           x0 = -1;
         } else if (xOffset > 16) {
            xPixelChange = 16 - (xOffset % 16); // xpos - xPixelChange
           x0 = (-xOffset - xPixelChange) >> 4;
         }
      } else if (xOffset < 0) {
         if (xOffset >= -16) {
            xPixelChange = xOffset; // xpos + xPixelChange
           x0 = 0;
         } else if (xOffset < -16) {
            xPixelChange = xOffset % 16; // xpos + xPixelChange
           x0 = -xOffset >> 4;
         }
      }
     
      int deltaX = -xOffset + screen.width;
      x1 = deltaX + (16 - (deltaX % 16)) >> 4;
     
      for (int y = y0, yTilePos = 0; y < y1; y++, yTilePos++) {
         int yAbsPos = yTilePos << 4;
         
         if (yOffset > 0) {
            yAbsPos += yPixelChange;
         } else if (yOffset < 0){
            yAbsPos -= yPixelChange;
         }
         
         for (int x = x0, xTilePos = 0; x < x1; x++, xTilePos++) {
            int xAbsPos = xTilePos << 4;
           
            if (xOffset > 0) {
               xAbsPos -= xPixelChange;
            } else if (xOffset < 0) {
               xAbsPos += xPixelChange;
            }
           
            getTile(x, y).render(xAbsPos, yAbsPos, screen);
         }
      }
   }
   
   public Tile getTile(int x, int y) {  
      if (x < 0 || y < 0 || x >= width || y >= height) return Tile.nil;
     
      int tile = tiles[x + y * width];
     
      if (tile == 0) {
         return Tile.grass;
      } else if (tile == 1) {
         return Tile.water;
      } else {
         return Tile.nil;
      }
   }
}


Screen.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
package com.mizzath.sandbox.screen;

import com.mizzath.sandbox.tile.Tile;

public class Screen {
   public int width, height;
   public int[] pixels;
   public int xOffset, yOffset;
   
   public Screen(int width, int height) {
      this.width = width;
      this.height = height;
      pixels = new int[width * height];
   }
   
   public void wipe() {
      for (int i = 0; i < pixels.length; i++) {
         pixels[i] = 0xffffff;
      }
   }
   
   /* Renders a tile.
    *
    * xp = the starting pixel's x position
    * yp = the starting pixel's y position
    * xa = the x position of the pixel being rendered
    * ya = the y position of the pixel being rendered
    * tile = the tile to be rendered
    */

   
   public void renderTile(int xp, int yp, Tile tile) {      
      for (int y = 0; y < tile.size; y++) {
         int ya = y + yp;
         if (ya < 0 || ya >= height) continue;

         for (int x = 0; x < tile.size; x++) {
            int xa = x + xp;
            if (xa < 0 || xa >= width) continue;
           
            pixels[xa + ya * width] = tile.colour;
         }
      }
   }
}


Sorry for dumping all of that upon you, and in no way are you required to meander through all of it. However, if you have some spare time, why not rummage though my predicament?

Thanks.

-Miles
Offline zngga
« Reply #1 - Posted 2013-02-04 05:01:53 »

What graphics lib are you using? I am assuming it is Java2D, yes? If you are, then take a look at BufferCapabilities (off the top of my head) not sure if there is an option there or not, I haven't used Java2D in a long time. Wink

Other than than, with my quick scan of your code, I didn't find any logic errors.

My code never has bugs... it just develops unexpected features!
Offline Mizzath

Senior Newbie





« Reply #2 - Posted 2013-02-04 05:14:54 »

A few things:

1. Java2D is the current library.
2. I am using a triple buffer.

Oh, and a bit more code to brighten your day.  Grin

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
package com.mizzath.sandbox;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

import javax.swing.JFrame;

import com.mizzath.sandbox.input.Keyboard;
import com.mizzath.sandbox.level.Level;
import com.mizzath.sandbox.screen.Screen;

public class Sandbox implements Runnable {
   final int WIDTH = 300;
   final int HEIGHT = WIDTH * 9 / 16;
   final int SCALE = 3;
   
   private String title = "Sandbox";
   
   private JFrame frame;
   private Canvas canvas;
   private BufferStrategy bufferStrategy;
   private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
   private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
   
   private Screen screen;
   private Level level;
   private Keyboard keyboard;
   
   boolean running = true;
   
   public Sandbox() {
      Dimension size = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
     
      frame = new JFrame(title);
      frame.setPreferredSize(size);
     
      canvas = new Canvas();
     
      frame.add(canvas);

      canvas.setPreferredSize(size);
     
      keyboard = new Keyboard();
      canvas.addKeyListener(keyboard);

      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setResizable(false);
      frame.setIgnoreRepaint(true);
      frame.pack();
      frame.setVisible(true);
     
      canvas.requestFocus();
     
      canvas.createBufferStrategy(3);
      bufferStrategy = canvas.getBufferStrategy();
     
      screen = new Screen(WIDTH, HEIGHT);
      level = new Level(64, 64, screen);
   }
   
   public void run() {
      long lastTime = System.nanoTime();
      double timer = System.currentTimeMillis();
      double ns = 1000000000.0 / 60;
      double delta = 0;
      int frames = 0;
      int updates = 0;
     
      while (running) {
         long now = System.nanoTime();
         delta += (now - lastTime) / ns;
         
         lastTime = now;
         
         while (delta >= 1) {
            delta -= 1;
            update();
            updates++;
         }
         
         frames++;
         render();
         
         if (System.currentTimeMillis() - timer >= 1000) {
            timer = System.currentTimeMillis();
            frame.setTitle(title + " || Updates: " + updates + ", FPS: " + frames);
            updates = 0;
            frames = 0;
         }
      }
   }
   
   private void render() {
      Graphics g = null;
     
      try {
         g = bufferStrategy.getDrawGraphics();
         render(g);
      } finally {
         g.dispose();
      }

      bufferStrategy.show();  
   }
   
   public void render(Graphics g) {
      screen.wipe();
      level.render(keyboard.x, keyboard.y);
     
      for (int i = 0; i < pixels.length; i++) {
         pixels[i] = screen.pixels[i];
      }
     
      g.drawImage(image, 0, 0, canvas.getWidth(), canvas.getHeight(), null);
      g.setColor(Color.CYAN);
      g.drawString("X: " + -keyboard.x + ", Y: " + -keyboard.y, 10, 10);
   }
   
   public void update() {
     
   }
   
   public static void main(String[] args) throws Exception {
      Sandbox sandbox = new Sandbox();
      new Thread(sandbox).start();
   }
}
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline zngga
« Reply #3 - Posted 2013-02-04 05:28:57 »

I just checked out the java docs for BufferedCapabilities, and there is no vsync option there, the only way to get vsync in Java2D that I know of is to use a GraphicsDevice and a GraphicsEnvironment, then set fullscreen and it is enabled by default (I think)

Aside from that the first thing you should do is cast your Graphics g into a Graphics2D object

1  
Graphics2D g = (Graphics2D) bufferStategy.getDrawGraphics();

My code never has bugs... it just develops unexpected features!
Offline Mizzath

Senior Newbie





« Reply #4 - Posted 2013-02-04 05:43:01 »

I just checked out the java docs for BufferedCapabilities, and there is no vsync option there, the only way to get vsync in Java2D that I know of is to use a GraphicsDevice and a GraphicsEnvironment, then set fullscreen and it is enabled by default (I think)

Aside from that the first thing you should do is cast your Graphics g into a Graphics2D object

1  
Graphics2D g = (Graphics2D) bufferStategy.getDrawGraphics();


What about Toolkit.getDefaultToolkit().sync()?
Offline CommanderKeith
« Reply #5 - Posted 2013-02-04 05:44:50 »

Hi mizzath,
Java 2d has no functionality to mitigate screen tearing. But java fx has, and the user shannon smith tried it out and reported his results in a thread on these forums. I would search it for you but I'm on my phone right now.
Alternatively, instead of switching to java fx and it's scene graph, you could use open gl or libgdx which can be used to get rid of screen tear.
Cheers, keith

Offline Mizzath

Senior Newbie





« Reply #6 - Posted 2013-02-04 05:56:44 »

You know, I may as well attempt a dive into libgdx. Thanks for the helpful pointers.
Offline Alan_W

JGO Knight


Medals: 8
Projects: 3


Java tames rock!


« Reply #7 - Posted 2013-02-04 06:08:45 »

Maybe the attempt to get a Hardware Double Buffer failed.  It doesn't always succeed, particularly when running windowed (or if there isn't any spare graphics memory).  Check BufferCapabilities.isFlipping().  You can use BufferStrategy.GetCapabilities() to obtain the capabilities structure.  Also check whether your memory card has enough memory to double buffer at your chosen screen resolution.

If it's double buffering by blitting (spell checker keeps changing that to blotting - bad spell check, down boy, down boy), you will get tearing when the window gets above a certain size.  It's most noticeable if you have sideways scrolling that scrolls in big increments, or objects that move very fast.

On the whole, I've been most successful with BufferStrategy when going fullscreen.  Otherwise, particularly for applets, I often just draw onto a bufferedImage and then draw that to the screen with one call.  It still tears if the image is big enough.  I rely on not clearing the background every frame and a high frame rate, so that the image doesn't change much between frames.  Admittedly I got into that habit for writing 4k games.  The BufferStrategy method ought to be better as in some circumstances one might get a hardware double buffer.






Time flies like a bird. Fruit flies like a banana.
Offline Mizzath

Senior Newbie





« Reply #8 - Posted 2013-02-04 07:25:36 »

Maybe the attempt to get a Hardware Double Buffer failed.  It doesn't always succeed, particularly when running windowed (or if there isn't any spare graphics memory).  Check BufferCapabilities.isFlipping().  You can use BufferStrategy.GetCapabilities() to obtain the capabilities structure.  Also check whether your memory card has enough memory to double buffer at your chosen screen resolution.

If it's double buffering by blitting (spell checker keeps changing that to blotting - bad spell check, down boy, down boy), you will get tearing when the window gets above a certain size.  It's most noticeable if you have sideways scrolling that scrolls in big increments, or objects that move very fast.

On the whole, I've been most successful with BufferStrategy when going fullscreen.  Otherwise, particularly for applets, I often just draw onto a bufferedImage and then draw that to the screen with one call.  It still tears if the image is big enough.  I rely on not clearing the background every frame and a high frame rate, so that the image doesn't change much between frames.  Admittedly I got into that habit for writing 4k games.  The BufferStrategy method ought to be better as in some circumstances one might get a hardware double buffer.


Sounds good; however, how does one debug efficiently whilst working in full screen mode? I generally enjoy the convenience of being able to see the console and write various messages to it.
Offline theagentd
« Reply #9 - Posted 2013-02-04 09:13:19 »

Uh. This isn't screen tearing. You can't print-screen screen tearing, because it occurs when the GPU finishes a frame while the screen is refreshing. Also, screen tearing is horizontal. >_>

Myomyomyo.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Cero
« Reply #10 - Posted 2013-02-04 13:32:32 »

Uh. This isn't screen tearing. You can't print-screen screen tearing, because it occur when the GPU finishes a frame while the screen is refreshing. Also, screen tearing is horizontal. >_>

This. :)
Tearing looks like: Someone takes the upper half of the picture and the lower half and rips them out of sync so to speak.

Offline theagentd
« Reply #11 - Posted 2013-02-04 15:03:59 »

Uh. This isn't screen tearing. You can't print-screen screen tearing, because it occur when the GPU finishes a frame while the screen is refreshing. Also, screen tearing is horizontal. >_>

This. Smiley
Tearing looks like: Someone takes the upper half of the picture and the lower half and rips them out of sync so to speak.
To clarify, you see a clear "tear" somewhere caused by the top half having the previous frame and the bottom half having the current frame. It's not visible unless the screen is changing.

Myomyomyo.
Offline Mizzath

Senior Newbie





« Reply #12 - Posted 2013-02-04 17:27:12 »

Alright, fair enough. However: do you have words of advice on how I go about fixing this?
Offline Varkas
« Reply #13 - Posted 2013-02-04 21:17:00 »

Might be tricky. Your rendering code looks quite complex to me, it's easily possible that there is something non-obvious going wrong there.

There is a second glitch in your screenshot. It's a bit smaller than the highlighted one.

Ideas:

- dump the tile positions to stdout and check if they match your expectations
- check your tile.render() method if it draws outside the expected are
- change the tile.render() method to show outlined squares so you can see overlaps better
- check if there is a race condition in your painting code (only if you use several threads)

if (error) throw new Brick(); // Blog (german): http://gedankenweber.wordpress.com
Offline CommanderKeith
« Reply #14 - Posted 2013-02-04 22:30:41 »

Lol, I didn't realise that the screen shot square showed the 'tear'. Looks like there's another one a little lower and to the left.
I'd say you're not drawing straight lines and that's why the edge is jagged.
I agree with varkas that you should just print the points you're drawing and look for the one with a strange y-coord.
Good luck!

Offline GabrielBailey74
« Reply #15 - Posted 2013-02-04 22:31:37 »

Looks like you're using a JFrame o.O.
Anytime I use a BufferedImage/Pixels or MemoryImageSource/Pixels on a JFrame I always Override the JFrame's following methods:

1  
2  
public void paint(Graphics g) { }
public void update(Graphics g) { }


Usually prevents screen tearing with pixel[] arrays.


Offline Z-Man
« Reply #16 - Posted 2013-02-05 03:05:06 »

When your using a buffer strategy you really should put it in a do-while to make sure your not losing buffer contents. For example your render method would look something like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
private void render() {
   do
   {
      do
      {
         Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
         render(g);
         g.dispose();
      }while(bufferStrategy.contentsRestored());
   
      strategy.show();
   }while(bufferStrategy.contentsLost());
}
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

BurntPizza (22 views)
2014-09-19 03:14:18

Dwinin (36 views)
2014-09-12 09:08:26

Norakomi (65 views)
2014-09-10 13:57:51

TehJavaDev (91 views)
2014-09-10 06:39:09

Tekkerue (45 views)
2014-09-09 02:24:56

mitcheeb (66 views)
2014-09-08 06:06:29

BurntPizza (49 views)
2014-09-07 01:13:42

Longarmx (36 views)
2014-09-07 01:12:14

Longarmx (41 views)
2014-09-07 01:11:22

Longarmx (38 views)
2014-09-07 01:10:19
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!