java 如何使我的swing图形项目具有更高的FPS(每秒帧数,对于不知道这意味着什么的人来说)?

lh80um4z  于 2022-10-30  发布在  Java
关注(0)|答案(1)|浏览(146)

我开始重新制作我的图形引擎,我在重新绘制JFrame上的VolatileImage时遇到了问题,我添加了一个FPS计数器,当它在全屏时,它只达到大约250或280 FPS,即使我在一个新的线程中有循环,我只在屏幕上绘制2个点。以下是Window类中的init和render函数的代码,可能有点杂乱无章:`

public int render() {
    frame.getGraphics().drawImage(vImg, 0, 0, frame.getWidth(), frame.getHeight(), null);
    totalFrames++;
    if (System.nanoTime() > lastFPScheck + 1000000000) {
        lastFPScheck = System.nanoTime();
        currentFPS = totalFrames;
        totalFrames = 0;
    }
    if (vImg.validate(gc) == VolatileImage.IMAGE_OK) {
        vImg = gc.createCompatibleVolatileImage(frame.getWidth(), frame.getHeight());
    }
    if (debugging()) {
        frame.setTitle(title + "[FPS: " + currentFPS + "]");
    }
    graphics = (Graphics2D)vImg.getGraphics();

    graphics.setColor(bg_color);
    graphics.fillRect(0, 0, frame.getWidth(), frame.getHeight());
    return currentFPS;
}

public void repaint_buffer() {
    frame.getContentPane().getGraphics().drawImage(vImg, 0, 0, canvasWidth, canvasHeight, null);
}

public void init(int width,
                 int height,
                 String title,
                 Color bg_color,
                 Consumer<Void> onDestroy) {
    this.canvasWidth = width;
    this.canvasHeight = height;
    this.is_open = true;
    this.title = title;
    this.bg_color = bg_color;

    this.frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
            onDestroy.accept(null);
        }
    });
    this.frame.addWindowListener(new WindowAdapter() {
        public void windowResized(WindowEvent e) {
            vImg = gc.createCompatibleVolatileImage(frame.getWidth(), frame.getHeight());
        }
    });
    this.frame.setLayout(null);
    this.frame.setSize(canvasWidth, canvasHeight);
    this.frame.addKeyListener(keyInput);
    this.frame.setResizable(true);
    this.frame.setLocation(-7, 0);
    this.frame.setTitle(title);
    this.frame.setVisible(true);

    gc = this.frame.getGraphicsConfiguration();
    vImg = gc.createCompatibleVolatileImage(this.frame.getWidth(), this.frame.getHeight());
    this.graphics = (Graphics2D) vImg.getGraphics();
}

'这是Main类的代码,GraphicsManager对象只包含set_pixel函数,该函数设置颜色并在给定位置绘制一个大小为1x 1像素的矩形:

package obsidian.core;
import obsidian.core.Window;

import java.awt.*;

public class Main {
    private static void on_destroy() {
        System.exit(0);
    }

    public static void main(String[] args) {
        Window window = new Window();
        window.init(Window.get_max_size().width, Window.get_max_size().height, "title", new Color(0, 0, 0), destroy -> on_destroy());

        GraphicsManager gm = new GraphicsManager(window);
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while(window.is_open()) {
                    window.render();

                    gm.draw_pixel(0.5, 0.5, new Color(255, 0, 0));
                    gm.draw_pixel(0, 0, new Color(255, 255, 255));

                    System.out.println(window.get_current_fps());
                }
            }
        });

        t.start();

    }
}

我试着移除VolatileImage并直接在框架上绘制,但是图像的填充使它不稳定,但是它达到了大约2000 FPS,IntelliJ IDEA跟不上。我能找到的唯一使它运行得更快的解决方案是移除VolatileImage并直接在框架上绘制。没有起作用;有人能帮我吗?谢谢

ryevplcw

ryevplcw1#

摇摆“被动”绘画

这将使用Swing Timer
每秒约170帧

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Timer timer;
        private Instant lastTick;
        private int fps = 0;

        public TestPane() {
        }

        @Override
        public void addNotify() {
            super.addNotify();
            if (timer != null) {
                timer.stop();
            }
            timer = new Timer(5, new ActionListener() {
                private int frameCount = 0;

                @Override
                public void actionPerformed(ActionEvent e) {
                    if (lastTick != null) {
                        Duration duration = Duration.between(lastTick, Instant.now());
                        if (duration.toMillis() >= 1000) {
                            lastTick = Instant.now();
                            fps = frameCount;
                            frameCount = 0;
                        } else {
                            frameCount++;
                        }
                    } else {
                        lastTick = Instant.now();
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public void removeNotify() {
            if (timer != null) {
                timer.stop();
            }
            timer = null;
            super.removeNotify();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            g2d.fillOval(20, 20, 20, 20);
            g2d.fillOval(40, 40, 20, 20);

            FontMetrics fm = g2d.getFontMetrics();
            String text = Integer.toString(fps);
            g2d.drawString(text, 10, fm.getAscent());
            g2d.dispose();
        }

    }
}

通过BufferStrategy进行“主动”疼痛

每秒约680帧

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;

public class Other {
    public static void main(String[] args) {
        new Other();
    }

    public Other() {
        Frame frame = new Frame();
        MainCanvas canvas = new MainCanvas();
        frame.setBackground(Color.BLUE);
        frame.add(canvas);
        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setVisible(true);
    }

    public class MainCanvas extends Canvas {

        private Thread thread;
        private AtomicBoolean running = new AtomicBoolean(false);
        private int fps;

        public MainCanvas() {
        }

        @Override
        public void addNotify() {
            super.addNotify();
            createBufferStrategy(3);
            start();
        }

        @Override
        public void removeNotify() {
            stop();
            super.removeNotify();
        }

        public void start() {
            stop();
            run();
        }

        protected void stop() {
            if (running.get()) {
                running.set(false);
            }
            thread = null;
        }

        protected void run() {
            stop();

            running.set(true);
            thread = new Thread(new Runnable() {
                private Instant lastTick;
                private int frameCount;

                @Override
                public void run() {
                    while (running.get()) {
                        if (lastTick != null) {
                            Duration duration = Duration.between(lastTick, Instant.now());
                            if (duration.toMillis() >= 1000) {
                                lastTick = Instant.now();
                                fps = frameCount;
                                frameCount = 0;
                            } else {
                                frameCount++;
                            }
                        } else {
                            lastTick = Instant.now();
                        }
                        render();
                        // Comment out these lines and see where it takes you
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException ex) {
                            java.util.logging.Logger.getLogger(Other.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                        }
                    }
                }
            });
            thread.start();
        }

        protected void render() {
            BufferStrategy bs = getBufferStrategy();
            while (bs == null) {
                bs = getBufferStrategy();
            }
            do {
                // The following loop ensures that the contents of the drawing buffer
                // are consistent in case the underlying surface was recreated
                do {
                    // Get a new graphics context every time through the loop
                    // to make sure the strategy is validated
                    Graphics2D g2d = (Graphics2D) bs.getDrawGraphics();

                    g2d.setColor(Color.GREEN);
                    g2d.fillRect(0, 0, getWidth(), getHeight());
                    // Render to graphics
                    // ...
                    g2d.setColor(Color.RED);
                    g2d.fillOval(20, 20, 20, 20);
                    g2d.fillOval(40, 40, 20, 20);

                    FontMetrics fm = g2d.getFontMetrics();
                    String text = Integer.toString(fps);
                    g2d.drawString(text, 10, fm.getAscent());
                    // Dispose the graphics
                    g2d.dispose();

                    // Repeat the rendering if the drawing buffer contents
                    // were restored
                } while (bs.contentsRestored());

                // Display the buffer
                bs.show();

                // Repeat the rendering if the drawing buffer was lost
            } while (bs.contentsLost());
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }
    }
}

免责声明

到目前为止,这只是一个“科学”示例,只是为了演示被动渲染工作流和主动渲染工作流

相关问题