Почему изменение ограничения FPS влияет на производительность рендеринга Graphics2D?JAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Почему изменение ограничения FPS влияет на производительность рендеринга Graphics2D?

Сообщение Anonymous »

Я работаю с холстом AWT с тройной буферизацией BufferStrategy. Приложение запускает цикл, который визуализирует экран с установленным максимальным значением FPS. В частности, я рисую несколько персонажей на экране по одному. Я пытаюсь измерить время, необходимое для рендеринга символов, но по какой-то причине кажется, что фактическое рисование символов занимает больше времени, когда ограничение FPS равно 60, а не 120. Вот изолированный тестовый пример:

Код: Выделить всё

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;

public class BufferStrategyTimingTest {

private static final int TARGET_FPS = 60; // Try 60, 120 to see the issue
private static final long TARGET_FRAME_TIME_NS = 1_000_000_000L / TARGET_FPS;

private static final int COLS = 80;
private static final int ROWS = 40;
private static final int CHAR_WIDTH = 12;
private static final int CHAR_HEIGHT = 20;
private static final int CANVAS_WIDTH = COLS * CHAR_WIDTH;
private static final int CANVAS_HEIGHT = ROWS * CHAR_HEIGHT;

private static volatile boolean running = true;

public static void main(String[] args) {
Frame frame = new Frame("BufferStrategy Timing Test - Target FPS: " + TARGET_FPS);
Canvas canvas = new Canvas();
canvas.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);

frame.add(canvas);
frame.pack();
frame.setLocationRelativeTo(null);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
running = false;
System.exit(0);
}
});
frame.setVisible(true);

// Initialize triple buffer strategy
canvas.setIgnoreRepaint(true);
canvas.createBufferStrategy(3);
BufferStrategy bufferStrategy = canvas.getBufferStrategy();

// Prepare font
Font font = new Font("Monospaced", Font.PLAIN, 12);
char[] charBuffer = new char[1];

// Timing variables
long frameCount = 0;
long lastFpsCheckTime = System.nanoTime();
long totalRenderUs = 0;
long totalDrawCharsUs = 0;

System.out.println("=== BufferStrategy Timing Test ===");
System.out.println("Target FPS: " + TARGET_FPS);
System.out.println("Frame budget: " + (TARGET_FRAME_TIME_NS / 1000) + " µs");
System.out.println("Canvas: " + COLS + "x" + ROWS + " = " + (COLS * ROWS) + " characters");
System.out.println("\nWaiting for stable FPS...\n");

// Game loop
while (running) {
long frameStartTime = System.nanoTime();

// Render
long renderStartTime = System.nanoTime();
long drawCharsStartTime = 0;
long drawCharsEndTime = 0;

do {
do {
Graphics2D g2 = (Graphics2D) bufferStrategy.getDrawGraphics();
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
g2.setFont(font);
g2.setColor(Color.WHITE);

// Time the actual drawChars calls
drawCharsStartTime = System.nanoTime();

// Draw characters one at a time
int fontAscent = g2.getFontMetrics().getAscent();
for (int y = 0; y < ROWS; y++) {
for (int x = 0; x < COLS;  x++) {
charBuffer[0] = (char) (33 + ((x + y) % 94));
int cellX = x * CHAR_WIDTH;
int cellY = y * CHAR_HEIGHT;
g2.drawChars(charBuffer, 0, 1, cellX, cellY + fontAscent);
}
}

drawCharsEndTime = System.nanoTime();

g2.dispose();
} while (bufferStrategy.contentsRestored());

bufferStrategy.show();
} while (bufferStrategy.contentsLost());

long renderEndTime = System.nanoTime();

long renderTimeUs = (renderEndTime - renderStartTime) / 1000;
long drawCharsTimeUs = (drawCharsEndTime - drawCharsStartTime) / 1000;

totalRenderUs += renderTimeUs;
totalDrawCharsUs += drawCharsTimeUs;
frameCount++;

// Report every 60 frames
if (frameCount % 60 == 0) {
long currentTime = System.nanoTime();
long elapsedNs = currentTime - lastFpsCheckTime;
int actualFPS = (int) (60_000_000_000L / elapsedNs);
lastFpsCheckTime = currentTime;

long avgRenderUs = totalRenderUs / 60;
long avgDrawCharsUs = totalDrawCharsUs / 60;

System.out.printf("[%2d FPS] Total render: %5d µs | drawChars: %5d µs%n",
actualFPS, avgRenderUs, avgDrawCharsUs);

totalRenderUs = 0;
totalDrawCharsUs = 0;
}

// Frame rate limiting
long frameTime = System.nanoTime() - frameStartTime;
long sleepTime = TARGET_FRAME_TIME_NS - frameTime;

if (sleepTime > 0) {
try {
Thread.sleep(sleepTime / 1_000_000L, (int)(sleepTime % 1_000_000L));
} catch (InterruptedException e) {
running = false;
}
}
}
}
}
Средние результаты печати:
  • Linux Mint / Geforce RTX, ограничение 60 кадров в секунду: [59 кадров в секунду] Общее время рендеринга: 5250 мкс | drawChars: 5250 мкс
  • Linux Mint / Geforce RTX, ограничение 120 кадров в секунду: [59 кадров в секунду] Общее время рендеринга: 2000 мкс | drawChars: 2000 мкс
  • Windows 11 / Intel Интегрированное ограничение 60 кадров в секунду: [57 кадров в секунду] Общее время рендеринга: 8000 мкс | drawChars: 4000 мкс
  • Windows 11 / Intel Интегрированное ограничение 120 кадров в секунду: [109 кадров в секунду] Общее время рендеринга: 6750 мкс | drawChars: 3250 мкс
Это приблизительные средние значения, просматриваемые через консоль. Общее время рендеринга в Linux на самом деле всегда примерно на 25 мкс больше, чем у drawChars. Эти значения соответствуют ±1000 мкс с очень редкими скачками вверх или вниз. Обе системы имеют мониторы с частотой 60 Гц.
Машина Windows тратит значительное количество времени на bufferStrategy.show(), что приводит к разнице между общим временем рендеринга и временем drawChars, но это не тема этого вопроса. Хотя разница меньше, компьютер с Windows также тратит меньше времени на рисование символов, когда предел частоты кадров выше. Что может быть причиной этого?

Подробнее здесь: https://stackoverflow.com/questions/798 ... erformance
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»