论坛首页 Java企业应用论坛

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

浏览 11091 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-01-13  
diggywang 写道
首先,我承认,qianhd是装*B的,即使不是,那也没有丝毫道德可言。阿门,原谅我这么说。

楼主所谓的大图片三缓冲,对内存可是高要求的!

嘿嘿,没办法啊,让用户界面不动与牺牲内存让界面流畅,得有个选择。
0 请登录后投票
   发表时间:2011-01-13  
sky3380 写道
我使用过与楼主类似的三缓冲技术,首先我确认的确可以提高界面刷新的速度,但这个技术也存在一个问题:如果界面不断变大,内存里的image就会跟着变大,导致内存占用不断上升,而swing的双缓冲机制就不会有这个问题,一直没搞明白怎么实现的。

嗯,里面写了个判断,当缓冲图片的大小与当前窗口不一样时,会重新创建一个新的缓冲图片。
由于Java对内存的回收不受用户控制,所以,很无奈,据说BufferedImage还有Bug,这个问题只有等以后JDK更新看看会不会解决吧。我的知识水平也只能了解下用户级别的东西了。
0 请登录后投票
   发表时间:2011-01-13  
qianhd 写道
JE帐号 写道
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.

三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.

我用我的机子跑了一下,LZ的代码跑下来两种方式没说呢么差别,但是我把if (len < 0.5) 改成if (len < 0.1) 后,确实是三重缓冲这种方式更快.

比较奇怪为什么会这样?难道说基于显卡计算的三重缓冲和基于CPU计算的三重缓冲有这样的差异?


显卡三重缓冲是针对打开垂直同步以后 如果帧数小于刷新率 帧数会被限制到1/2刷新率 1/3 甚至更低
引入此技术是为了解决这个问题
跟这个帖子中的三缓冲完全是两码事


还是没能从本质解释我的困惑,我的疑惑是这样的.

如果三重缓冲,确实比双缓冲更有利于速度,那么为什么显卡优化的三重缓冲并不比双缓冲快?事实上通常是把三重缓冲作为垂直同步的替代方案,用来保持画面刷新的帧数与双缓冲保持一致而又不产生画面的撕裂感.

如果说基于GPU运算的三缓冲和基于CPU运算的三缓冲不同,但是就我的理解,他们的基本原理又确实是差不多一样的,这两者有什么本质的区别么?

求了解的人解答.
0 请登录后投票
   发表时间:2011-01-13  
JE帐号 写道
qianhd 写道
JE帐号 写道
我不是做游戏或者图像显示的,但是以前玩游戏调优显卡设置时,曾经了解过一些三重缓冲.

三重缓冲不是为了提高画面的帧数,原理上讲三重缓冲和双缓冲在帧数上应该差不多.三重缓冲主要是为了解决使用双缓冲时带来的画面的撕裂感,最初时为了解决这种撕裂感,都是开启垂直同步选项以便人为的控制画面刷新节奏,但是这个选项会大大的降低画面的帧数,所以才又出现三重缓冲这种通过增加资源消耗但是不人为去控制画面刷新节奏的方法.

我用我的机子跑了一下,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缓冲->屏幕
运用程序级的缓冲还有可能再次使用多级,如一张图是由多个部分组成的,在些部分是常变化的,而另一些部分却不常变化,所以在这里可以对不常变化的进行缓冲,看实际情况而定。
0 请登录后投票
   发表时间:2011-01-13  
jorneyR 写道
dearsunkey 写道
所谓双缓冲不就是第一次内存绘制,第二次一次性展示到屏幕上吗? 如果是这种逻辑,那么你三缓冲不但不能提高效率反而会大打折扣,不是吗? 个人拙见

不一样,例如给图片处理效果时,要进行大量的计算,如果你是直接去操作BufferedImage的像素,setRGB或者操作它的Raster,这时候速度就不如用PixelGrabber取得像素数据,处理完后再用MemoryImageSource生成图片快。

因为对Swing back-buffer的操作需要用到更多的内部调用与内部绘制的操作协调等,需要更多的资源,而对BufferedImage操作,系统不关心你在做什么,也不知道你在做什么,它只关心一次性绘制图片,所以速度上才会快一点。


所以我觉得,其实这里的所谓三缓冲高效,本质是指用独立缓冲区要比swing内部实现的缓冲消耗小.并不是说使用三缓冲这种方式带来的高效.个人怀疑,如果swing不使用双缓冲,而自己实现一个双缓冲,可能比三缓冲差的也不多.

之所以纠结这个问题,是因为我所知道的三缓冲不是直接为了提高性能而诞生的,换句话说双缓冲和三缓冲的目的是不一样的.缓冲这东西只要有了一重,性能方面就会有个质的飞跃,再多一重,带来的可能只是实现细节级别的性能提升,不再是质的飞跃了.
0 请登录后投票
   发表时间:2011-01-14   最后修改: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();
            }
        }
    }

}
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics