别以为Swing有双缓冲就够了,变态时3缓冲是很有必要的
别以为Swing有双缓冲就够了,变态时三缓冲是很有必要的

AWT时代必须自己实现双缓冲机制,否则绘画时界面总是闪烁。
Swing的JComponent以及其子类的绘制默认是使用了双缓冲的,例如JPanel,方便了不少。
但是,当在Swing中绘制几千个图元时,如果绘图仍然是直接对Swing的Back-buffer进行操作,速度会非常的慢,甚至慢到没法忍受。例如下面的例子里有16385个点,共画16384条线,改变窗口的大小,就可以发现直接操作Swing的Back-buffer是多么的令人难以忍受。
这个时候,使用三缓冲(triple-buffer)是很有必要的:先把这些图元绘制到自己创建的缓冲图像里,然后再一次性的把此缓冲图像交给Swing后台绘制,速度的提升是非常非常的大的。
只有两个Java文件,就不打包了。
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
呵呵,就算我在扯蛋吧,没什么好争论的.
有时间可以去看看Swing方面的书,如Filthy Rich Client等之类的
装啥B啊 不就本30多块的书嘛
只可惜你看了也白看
你大部分的回帖都对人充满歧视,能不能守一点职业道德?
呵呵,就算我在扯蛋吧,没什么好争论的.
有时间可以去看看Swing方面的书,如Filthy Rich Client等之类的
装啥B啊 不就本30多块的书嘛
只可惜你看了也白看
你大部分的回帖都对人充满歧视,能不能守一点职业道德?
首先 我歧视谁了?
其次 数据说话 事实说话
再次 谁在装*B? 我贴数据证明 楼主说 "没啥好争 你不懂的 去看书."
尽信书不如无书 任何优化手段都是有应用场景的
是的,唯有独善其身
可能跟机器与系统有关吧,我的是MBP133,CPU双核2.4G,显卡GF 8600M。
下面是在我的机器上,Snow Leopard里的状态
len < 0.5:
三缓冲17毫秒左右
Swing双缓冲280毫秒左右
len < 0.1:
三缓冲200毫秒左右
Swing双缓冲8500毫秒左右,窗口出现忙的状态
没有在Linux和Windows里测试过,具体有没有效率不清楚,没测试过,^_^,什么也不敢说
不一样,例如给图片处理效果时,要进行大量的计算,如果你是直接去操作BufferedImage的像素,setRGB或者操作它的Raster,这时候速度就不如用PixelGrabber取得像素数据,处理完后再用MemoryImageSource生成图片快。
因为对Swing back-buffer的操作需要用到更多的内部调用与内部绘制的操作协调等,需要更多的资源,而对BufferedImage操作,系统不关心你在做什么,也不知道你在做什么,它只关心一次性绘制图片,所以速度上才会快一点。
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
嗯,这个是逻辑上的三缓冲,不是硬件加速的三缓冲,差别还是比较大的。
嘿嘿,没办法啊,让用户界面不动与牺牲内存让界面流畅,得有个选择。
嗯,里面写了个判断,当缓冲图片的大小与当前窗口不一样时,会重新创建一个新的缓冲图片。
由于Java对内存的回收不受用户控制,所以,很无奈,据说BufferedImage还有Bug,这个问题只有等以后JDK更新看看会不会解决吧。我的知识水平也只能了解下用户级别的东西了。
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
还是没能从本质解释我的困惑,我的疑惑是这样的.
如果三重缓冲,确实比双缓冲更有利于速度,那么为什么显卡优化的三重缓冲并不比双缓冲快?事实上通常是把三重缓冲作为垂直同步的替代方案,用来保持画面刷新的帧数与双缓冲保持一致而又不产生画面的撕裂感.
如果说基于GPU运算的三缓冲和基于CPU运算的三缓冲不同,但是就我的理解,他们的基本原理又确实是差不多一样的,这两者有什么本质的区别么?
求了解的人解答.
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
还是没能从本质解释我的困惑,我的疑惑是这样的.
如果三重缓冲,确实比双缓冲更有利于速度,那么为什么显卡优化的三重缓冲并不比双缓冲快?事实上通常是把三重缓冲作为垂直同步的替代方案,用来保持画面刷新的帧数与双缓冲保持一致而又不产生画面的撕裂感.
如果说基于GPU运算的三缓冲和基于CPU运算的三缓冲不同,但是就我的理解,他们的基本原理又确实是差不多一样的,这两者有什么本质的区别么?
求了解的人解答.
我觉得应该是他们缓冲的对象不一样,原理都是先在一个缓冲图像里绘制完再一次性的绘制到屏幕上。
如GPU的缓冲里面,还有涉及如Video Cache(Swing也用了这个,不过只是针对Manged Image,动态修改的Buffered Image不会被它使用,系统会自动判断)
运用程序级缓冲->Swing back-buffer(等同AWT时代的离屏环境)->GPU缓冲->屏幕
运用程序级的缓冲还有可能再次使用多级,如一张图是由多个部分组成的,在些部分是常变化的,而另一些部分却不常变化,所以在这里可以对不常变化的进行缓冲,看实际情况而定。
不一样,例如给图片处理效果时,要进行大量的计算,如果你是直接去操作BufferedImage的像素,setRGB或者操作它的Raster,这时候速度就不如用PixelGrabber取得像素数据,处理完后再用MemoryImageSource生成图片快。
因为对Swing back-buffer的操作需要用到更多的内部调用与内部绘制的操作协调等,需要更多的资源,而对BufferedImage操作,系统不关心你在做什么,也不知道你在做什么,它只关心一次性绘制图片,所以速度上才会快一点。
所以我觉得,其实这里的所谓三缓冲高效,本质是指用独立缓冲区要比swing内部实现的缓冲消耗小.并不是说使用三缓冲这种方式带来的高效.个人怀疑,如果swing不使用双缓冲,而自己实现一个双缓冲,可能比三缓冲差的也不多.
之所以纠结这个问题,是因为我所知道的三缓冲不是直接为了提高性能而诞生的,换句话说双缓冲和三缓冲的目的是不一样的.缓冲这东西只要有了一重,性能方面就会有个质的飞跃,再多一重,带来的可能只是实现细节级别的性能提升,不再是质的飞跃了.
AWT时代必须自己实现双缓冲机制,否则绘画时界面总是闪烁。
Swing的JComponent以及其子类的绘制默认是使用了双缓冲的,例如JPanel,方便了不少。
但是,当在Swing中绘制几千个图元时,如果绘图仍然是直接对Swing的Back-buffer进行操作,速度会非常的慢,甚至慢到没法忍受。例如下面的例子里有16385个点,共画16384条线,改变窗口的大小,就可以发现直接操作Swing的Back-buffer是多么的令人难以忍受。
这个时候,使用三缓冲(triple-buffer)是很有必要的:先把这些图元绘制到自己创建的缓冲图像里,然后再一次性的把此缓冲图像交给Swing后台绘制,速度的提升是非常非常的大的。
只有两个Java文件,就不打包了。
import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; import util.GeometryUtil; @SuppressWarnings("serial") public class Growing extends JPanel { private List<Point2D> ps = new ArrayList<Point2D>(); private Timer timer; private boolean stopped = false; public Growing() { ps.add(new Point2D.Double(0, 0)); ps.add(new Point2D.Double(800, 0)); timer = new Timer(500, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { grow(); repaint(); } }); timer.start(); } public void grow() { if (stopped) { return; } List<Point2D> temp = new ArrayList<Point2D>(); temp.add(ps.get(0)); for (int i = 0; i < ps.size() - 1; ++i) { Point2D p0 = ps.get(i); Point2D p4 = ps.get(i + 1); double len = GeometryUtil.distanceOfPoints(p0, p4); if (len < 0.5) { // 当线条长度小于1时,就停止再增长 System.out.println(ps.size()); timer.stop(); return; } Point2D p1 = GeometryUtil.extentPoint(p0, p4, len / 3); Point2D p3 = GeometryUtil.extentPoint(p0, p4, len * 2 / 3); Point2D p2 = GeometryUtil.rotate(p3.getX(), p3.getY(), p1.getX(), p1.getY(), 60); temp.add(p1); temp.add(p2); temp.add(p3); temp.add(p4); } ps = null; ps = temp; temp = null; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; // 修改type的值使用不同的绘制方式,1为compatible image, 2为swing的back-buffer int type = 1; // 改变窗口的大小,可以看到直接对intermediate image操作比直接对swing back-buffer操作快很多. // 所以有很多绘制操作时,使用triple buffer是很有必要的(因为Swing已经默认使用了双缓冲). if (type == 1) { // [[[1]]]: 操作 compatible image 速度非常快 renderWithBuf(g2d, getWidth(), getHeight()); } else { // [[[2]]]: 操作Swing的 back-buffer 速度非常慢 render(g2d, getWidth(), getHeight()); } } private BufferedImage bufImg; protected void renderWithBuf(Graphics2D g2d, int w, int h) { if (bufImg == null || bufImg.getWidth() != w || bufImg.getHeight() != h) { bufImg = createCompatibleImage(w, h, Transparency.OPAQUE); // bufImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } Graphics2D gg = bufImg.createGraphics(); render(gg, w, h); gg.dispose(); g2d.drawImage(bufImg, 0, 0, null); } protected void render(Graphics2D g2d, int w, int h) { g2d.setBackground(Color.BLACK); g2d.clearRect(0, 0, w, h); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.translate(0, h - 20); g2d.setColor(Color.WHITE); for (int i = 0; i < ps.size() - 1; ++i) { Point2D sp = ps.get(i); Point2D ep = ps.get(i + 1); g2d.drawLine((int) sp.getX(), -(int) sp.getY(), (int) ep.getX(), -(int) ep.getY()); } } // 创建硬件适配的缓冲图像,为了能显示得更快速 public static BufferedImage createCompatibleImage(int w, int h, int type) { GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice device = env.getDefaultScreenDevice(); GraphicsConfiguration gc = device.getDefaultConfiguration(); return gc.createCompatibleImage(w, h, type); } private static void createGuiAndShow() { JFrame frame = new JFrame("Growing"); frame.getContentPane().add(new Growing()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(800, 400); frame.setAlwaysOnTop(true); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createGuiAndShow(); } }); } }
package util; import java.awt.geom.Point2D; public class GeometryUtil { // 两点之间的距离 public static double distanceOfPoints(Point2D p1, Point2D p2) { double disX = p2.getX() - p1.getX(); double disY = p2.getY() - p1.getY(); double dis = Math.sqrt(disX * disX + disY * disY); return dis; } // 两点的中点 public static Point2D middlePoint(Point2D p1, Point2D p2) { double x = (p1.getX() + p2.getX()) / 2; double y = (p1.getY() + p2.getY()) / 2; return new Point2D.Double(x, y); } // 在两点所在直线上,以从startPoint到endPoint为方向,离startPoint的距离disToStartPoint的点 public static Point2D extentPoint(Point2D startPoint, Point2D endPoint, double disToStartPoint) { double disX = endPoint.getX() - startPoint.getX(); double disY = endPoint.getY() - startPoint.getY(); double dis = Math.sqrt(disX * disX + disY * disY); double sin = (endPoint.getY() - startPoint.getY()) / dis; double cos = (endPoint.getX() - startPoint.getX()) / dis; double deltaX = disToStartPoint * cos; double deltaY = disToStartPoint * sin; return new Point2D.Double(startPoint.getX() + deltaX, startPoint.getY() + deltaY); } // 绕原点的旋转矩阵,绕任意点旋转,可以先移动到原点,旋转,然后再移回去 // cosθ -sinθ 0 // sinθ +conθ 0 // 0000 +0000 1 // x = r*cosα, y = r*sinα // x' = r*cos(α+θ) = r*cosα*cosθ - r*sinα*sinθ = x*cosθ - y*sinθ // y' = r*sin(α+θ) = r*sinα*cosθ + r*cosα*sinθ = x*sinθ + y*cosθ // (x, y)绕圆心旋转degree度 public static Point2D rotate(double x, double y, double degree) { return rotate(x, y, 0, 0, degree); } // (x, y)绕(ox, oy)旋转degree度 public static Point2D rotate(double x, double y, double ox, double oy, double degree) { x -= ox; y -= oy; double cos = Math.cos(Math.toRadians(degree)); double sin = Math.sin(Math.toRadians(degree)); double temp = x * cos - y * sin; y = x * sin + y * cos; x = temp; return new Point2D.Double(x + ox, y + oy); } public static void main(String[] args) { Point2D p = rotate(50, 10, 10); System.out.println(p); p = rotate(100, 60, 50, 50, 10); System.out.println(p); } }
6 楼
003
2011-01-13
写自己的代码,赚自己的银子,其它的都是浮云
7 楼
dearsunkey
2011-01-13
所谓双缓冲不就是第一次内存绘制,第二次一次性展示到屏幕上吗? 如果是这种逻辑,那么你三缓冲不但不能提高效率反而会大打折扣,不是吗? 个人拙见
8 楼
JE帐号
2011-01-13
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
9 楼
qianhd
2011-01-13
JE帐号 写道
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
10 楼
vision2000
2011-01-13
qianhd 写道
jorneyR 写道
qianhd 写道
有数据证明吗?
从原理上看 这完全是无稽之谈
从我测试的数据来看 更加是无稽之谈
为了让差距更加明显 我将初始尺寸改成1400*600 线条长度小于0.05
总共1048577个点
我加了一个线程 会定时修改frame的size
然后在paintComponent开始时记录时间,结束时候打印执行时间
所谓的3缓冲完全绘图一次 耗时1.5-1.8秒(这也时间太长了,我这里是毫秒级的)
默认双缓冲完全绘图一次 耗时1.5-1.8秒
没看出来所谓的3缓冲有任何的性能提升
从原理上看 这完全是无稽之谈
从我测试的数据来看 更加是无稽之谈
为了让差距更加明显 我将初始尺寸改成1400*600 线条长度小于0.05
总共1048577个点
我加了一个线程 会定时修改frame的size
然后在paintComponent开始时记录时间,结束时候打印执行时间
所谓的3缓冲完全绘图一次 耗时1.5-1.8秒(这也时间太长了,我这里是毫秒级的)
默认双缓冲完全绘图一次 耗时1.5-1.8秒
没看出来所谓的3缓冲有任何的性能提升
呵呵,就算我在扯蛋吧,没什么好争论的.
有时间可以去看看Swing方面的书,如Filthy Rich Client等之类的
装啥B啊 不就本30多块的书嘛
只可惜你看了也白看
你大部分的回帖都对人充满歧视,能不能守一点职业道德?
11 楼
sky3380
2011-01-13
我使用过与楼主类似的三缓冲技术,首先我确认的确可以提高界面刷新的速度,但这个技术也存在一个问题:如果界面不断变大,内存里的image就会跟着变大,导致内存占用不断上升,而swing的双缓冲机制就不会有这个问题,一直没搞明白怎么实现的。
12 楼
diggywang
2011-01-13
首先,我承认,qianhd是装*B的,即使不是,那也没有丝毫道德可言。阿门,原谅我这么说。
楼主所谓的大图片三缓冲,对内存可是高要求的!
楼主所谓的大图片三缓冲,对内存可是高要求的!
13 楼
ninja9turtle
2011-01-13
这本质说应该是属于业务级别的缓存,更前面还有显卡帮你做的缓存,更后面自然可以在内存操作根据自己业务特性进行缓存,甚至更后台数据存储部分也可以根据需要缓存
14 楼
qianhd
2011-01-13
vision2000 写道
qianhd 写道
jorneyR 写道
qianhd 写道
有数据证明吗?
从原理上看 这完全是无稽之谈
从我测试的数据来看 更加是无稽之谈
为了让差距更加明显 我将初始尺寸改成1400*600 线条长度小于0.05
总共1048577个点
我加了一个线程 会定时修改frame的size
然后在paintComponent开始时记录时间,结束时候打印执行时间
所谓的3缓冲完全绘图一次 耗时1.5-1.8秒
默认双缓冲完全绘图一次 耗时1.5-1.8秒
没看出来所谓的3缓冲有任何的性能提升
从原理上看 这完全是无稽之谈
从我测试的数据来看 更加是无稽之谈
为了让差距更加明显 我将初始尺寸改成1400*600 线条长度小于0.05
总共1048577个点
我加了一个线程 会定时修改frame的size
然后在paintComponent开始时记录时间,结束时候打印执行时间
所谓的3缓冲完全绘图一次 耗时1.5-1.8秒
默认双缓冲完全绘图一次 耗时1.5-1.8秒
没看出来所谓的3缓冲有任何的性能提升
呵呵,就算我在扯蛋吧,没什么好争论的.
有时间可以去看看Swing方面的书,如Filthy Rich Client等之类的
装啥B啊 不就本30多块的书嘛
只可惜你看了也白看
你大部分的回帖都对人充满歧视,能不能守一点职业道德?
首先 我歧视谁了?
其次 数据说话 事实说话
再次 谁在装*B? 我贴数据证明 楼主说 "没啥好争 你不懂的 去看书."
尽信书不如无书 任何优化手段都是有应用场景的
15 楼
003
2011-01-13
与其闲扯蛋,还不如把楼主帖的代码拿下来实实在在的跑一把试试,以下老三用数据说话。
楼主帖的原代码在我的机器上,双缓冲、三缓冲表现几乎一样,8000到8015毫秒
但是把结束条件改为if(len < 0.1),三缓冲的优势很明显,10000到10015毫秒,而双缓冲则用了14000到15000毫秒,这个时候更变态的还有一点,双缓冲的时候,timer结束后,点X去关闭窗口,点了之后要过5秒左右才能关闭,而三缓冲则是立竿见影瞬间关闭。
楼主帖的原代码在我的机器上,双缓冲、三缓冲表现几乎一样,8000到8015毫秒
但是把结束条件改为if(len < 0.1),三缓冲的优势很明显,10000到10015毫秒,而双缓冲则用了14000到15000毫秒,这个时候更变态的还有一点,双缓冲的时候,timer结束后,点X去关闭窗口,点了之后要过5秒左右才能关闭,而三缓冲则是立竿见影瞬间关闭。
16 楼
jorneyR
2011-01-13
003 写道
写自己的代码,赚自己的银子,其它的都是浮云
是的,唯有独善其身
17 楼
jorneyR
2011-01-13
003 写道
与其闲扯蛋,还不如把楼主帖的代码拿下来实实在在的跑一把试试,以下老三用数据说话。
楼主帖的原代码在我的机器上,双缓冲、三缓冲表现几乎一样,8000到8015毫秒
但是把结束条件改为if(len < 0.1),三缓冲的优势很明显,10000到10015毫秒,而双缓冲则用了14000到15000毫秒,这个时候更变态的还有一点,双缓冲的时候,timer结束后,点X去关闭窗口,点了之后要过5秒左右才能关闭,而三缓冲则是立竿见影瞬间关闭。
楼主帖的原代码在我的机器上,双缓冲、三缓冲表现几乎一样,8000到8015毫秒
但是把结束条件改为if(len < 0.1),三缓冲的优势很明显,10000到10015毫秒,而双缓冲则用了14000到15000毫秒,这个时候更变态的还有一点,双缓冲的时候,timer结束后,点X去关闭窗口,点了之后要过5秒左右才能关闭,而三缓冲则是立竿见影瞬间关闭。
可能跟机器与系统有关吧,我的是MBP133,CPU双核2.4G,显卡GF 8600M。
下面是在我的机器上,Snow Leopard里的状态
len < 0.5:
三缓冲17毫秒左右
Swing双缓冲280毫秒左右
len < 0.1:
三缓冲200毫秒左右
Swing双缓冲8500毫秒左右,窗口出现忙的状态
没有在Linux和Windows里测试过,具体有没有效率不清楚,没测试过,^_^,什么也不敢说
18 楼
jorneyR
2011-01-13
dearsunkey 写道
所谓双缓冲不就是第一次内存绘制,第二次一次性展示到屏幕上吗? 如果是这种逻辑,那么你三缓冲不但不能提高效率反而会大打折扣,不是吗? 个人拙见
不一样,例如给图片处理效果时,要进行大量的计算,如果你是直接去操作BufferedImage的像素,setRGB或者操作它的Raster,这时候速度就不如用PixelGrabber取得像素数据,处理完后再用MemoryImageSource生成图片快。
因为对Swing back-buffer的操作需要用到更多的内部调用与内部绘制的操作协调等,需要更多的资源,而对BufferedImage操作,系统不关心你在做什么,也不知道你在做什么,它只关心一次性绘制图片,所以速度上才会快一点。
19 楼
jorneyR
2011-01-13
qianhd 写道
JE帐号 写道
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
嗯,这个是逻辑上的三缓冲,不是硬件加速的三缓冲,差别还是比较大的。
20 楼
jorneyR
2011-01-13
diggywang 写道
首先,我承认,qianhd是装*B的,即使不是,那也没有丝毫道德可言。阿门,原谅我这么说。
楼主所谓的大图片三缓冲,对内存可是高要求的!
楼主所谓的大图片三缓冲,对内存可是高要求的!
嘿嘿,没办法啊,让用户界面不动与牺牲内存让界面流畅,得有个选择。
21 楼
jorneyR
2011-01-13
sky3380 写道
我使用过与楼主类似的三缓冲技术,首先我确认的确可以提高界面刷新的速度,但这个技术也存在一个问题:如果界面不断变大,内存里的image就会跟着变大,导致内存占用不断上升,而swing的双缓冲机制就不会有这个问题,一直没搞明白怎么实现的。
嗯,里面写了个判断,当缓冲图片的大小与当前窗口不一样时,会重新创建一个新的缓冲图片。
由于Java对内存的回收不受用户控制,所以,很无奈,据说BufferedImage还有Bug,这个问题只有等以后JDK更新看看会不会解决吧。我的知识水平也只能了解下用户级别的东西了。
22 楼
JE帐号
2011-01-13
qianhd 写道
JE帐号 写道
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
还是没能从本质解释我的困惑,我的疑惑是这样的.
如果三重缓冲,确实比双缓冲更有利于速度,那么为什么显卡优化的三重缓冲并不比双缓冲快?事实上通常是把三重缓冲作为垂直同步的替代方案,用来保持画面刷新的帧数与双缓冲保持一致而又不产生画面的撕裂感.
如果说基于GPU运算的三缓冲和基于CPU运算的三缓冲不同,但是就我的理解,他们的基本原理又确实是差不多一样的,这两者有什么本质的区别么?
求了解的人解答.
23 楼
jorneyR
2011-01-13
JE帐号 写道
qianhd 写道
JE帐号 写道
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.
我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.
比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?
显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事
还是没能从本质解释我的困惑,我的疑惑是这样的.
如果三重缓冲,确实比双缓冲更有利于速度,那么为什么显卡优化的三重缓冲并不比双缓冲快?事实上通常是把三重缓冲作为垂直同步的替代方案,用来保持画面刷新的帧数与双缓冲保持一致而又不产生画面的撕裂感.
如果说基于GPU运算的三缓冲和基于CPU运算的三缓冲不同,但是就我的理解,他们的基本原理又确实是差不多一样的,这两者有什么本质的区别么?
求了解的人解答.
我觉得应该是他们缓冲的对象不一样,原理都是先在一个缓冲图像里绘制完再一次性的绘制到屏幕上。
如GPU的缓冲里面,还有涉及如Video Cache(Swing也用了这个,不过只是针对Manged Image,动态修改的Buffered Image不会被它使用,系统会自动判断)
运用程序级缓冲->Swing back-buffer(等同AWT时代的离屏环境)->GPU缓冲->屏幕
运用程序级的缓冲还有可能再次使用多级,如一张图是由多个部分组成的,在些部分是常变化的,而另一些部分却不常变化,所以在这里可以对不常变化的进行缓冲,看实际情况而定。
24 楼
JE帐号
2011-01-13
jorneyR 写道
dearsunkey 写道
所谓双缓冲不就是第一次内存绘制,第二次一次性展示到屏幕上吗? 如果是这种逻辑,那么你三缓冲不但不能提高效率反而会大打折扣,不是吗? 个人拙见
不一样,例如给图片处理效果时,要进行大量的计算,如果你是直接去操作BufferedImage的像素,setRGB或者操作它的Raster,这时候速度就不如用PixelGrabber取得像素数据,处理完后再用MemoryImageSource生成图片快。
因为对Swing back-buffer的操作需要用到更多的内部调用与内部绘制的操作协调等,需要更多的资源,而对BufferedImage操作,系统不关心你在做什么,也不知道你在做什么,它只关心一次性绘制图片,所以速度上才会快一点。
所以我觉得,其实这里的所谓三缓冲高效,本质是指用独立缓冲区要比swing内部实现的缓冲消耗小.并不是说使用三缓冲这种方式带来的高效.个人怀疑,如果swing不使用双缓冲,而自己实现一个双缓冲,可能比三缓冲差的也不多.
之所以纠结这个问题,是因为我所知道的三缓冲不是直接为了提高性能而诞生的,换句话说双缓冲和三缓冲的目的是不一样的.缓冲这东西只要有了一重,性能方面就会有个质的飞跃,再多一重,带来的可能只是实现细节级别的性能提升,不再是质的飞跃了.
25 楼
regular
2011-01-14
提供一个仿制的SWT实现。性能比Swing的要差。
import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Transform; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import util.GeometryUtil; public class GrowingSWT { private final Shell shell; private final Canvas canvas; private List<Point2D> ps = new ArrayList<Point2D>(); private boolean stopped = false; Image image = null; int type = 1; private final Runnable timer = new Runnable() { public void run() { shell.getDisplay().timerExec(500, timer); grow(); canvas.redraw(); } }; GrowingSWT(final Display display) { shell = new Shell(display, SWT.DOUBLE_BUFFERED | SWT.SHELL_TRIM); shell.setLayout(new FillLayout()); canvas = new Canvas(shell, SWT.NULL); canvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { long time = System.currentTimeMillis(); if (type == 1) { e.gc.drawImage(getBuffer(false), 0, 0); } else { render(e.gc); } time = System.currentTimeMillis() - time; if (time > 10) { System.out.println(time); } } }); ps.add(new Point2D.Double(0, 0)); ps.add(new Point2D.Double(800, 0)); display.timerExec(500, timer); } Image getBuffer( boolean withRefresh) { Rectangle bounds = canvas.getBounds(); if (image == null || !image.getBounds().equals(bounds)) { image = new Image(shell.getDisplay(), bounds); renderWithBuffer(image); } else if (withRefresh) { renderWithBuffer(image); } return image; } public void grow() { if (stopped) { return; } List<Point2D> temp = new ArrayList<Point2D>(); temp.add(ps.get(0)); for (int i = 0; i < ps.size() - 1; ++i) { Point2D p0 = ps.get(i); Point2D p4 = ps.get(i + 1); double len = GeometryUtil.distanceOfPoints(p0, p4); if (len < 0.1) { // 当线条长度小于1时,就停止再增长 System.out.println(ps.size()); shell.getDisplay().timerExec(-1, timer); return; } Point2D p1 = GeometryUtil.extentPoint(p0, p4, len / 3); Point2D p3 = GeometryUtil.extentPoint(p0, p4, len * 2 / 3); Point2D p2 = GeometryUtil.rotate(p3.getX(), p3.getY(), p1.getX(), p1.getY(), 60); temp.add(p1); temp.add(p2); temp.add(p3); temp.add(p4); } ps = null; ps = temp; temp = null; if (type == 1) { getBuffer(true); } } private void renderWithBuffer(Image image) { GC _gc = new GC(image); render(_gc); _gc.dispose(); } private void render(GC gc) { Display display = shell.getDisplay(); gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); Rectangle bounds = canvas.getBounds(); gc.fillRectangle(bounds); gc.setAdvanced(true); gc.setAntialias(SWT.ON); Transform transform = new Transform(display); transform.translate(0, bounds.height - 20); gc.setTransform(transform); gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); for (int i = 0; i < ps.size() - 1; ++i) { Point2D sp = ps.get(i); Point2D ep = ps.get(i + 1); gc.drawLine((int) sp.getX(), -(int) sp.getY(), (int) ep.getX(), -(int) ep.getY()); } } public static void main(String[] args) { Display display = Display.getDefault(); Shell shell = new GrowingSWT(display).shell; shell.setSize(800, 400); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } }