Подтвердить что ты не робот

Strange BufferStrategy issue - Игра работает быстро только на графических процессорах Intel

У меня возникла очень странная проблема, я пытался найти ответ в течение нескольких дней и дней. В моей игре появилась новая система частиц, но она была слишком медленной, чтобы играть. К сожалению, преобразования BufferedImage очень медленные. Эффект взрыва состоит из около 200 белых спрайтов, загруженных из файла PNG, повернутых, масштабированных и окрашенных случайным образом, движущихся со случайной скоростью.

Я попытался повысить производительность с помощью тройной/двойной буферизации и столкнулся с некоторыми проблемами.

Моя первая попытка была с JPanel, на которую была нарисована игра. Я настроил буферы в классе JFrame (Main), затем сделал чертеж в классе Game (extans JPanel), НО, без Graphics g = bufferstrategy.getDrawGraphics();. Затем, в конце метода рисования, я показал буфер, если он не был потерян. Буфер всегда "терялся", поскольку я не рисовал с ним графический объект. Но! Игра проходит так же быстро, как черт! Без использования буфера в практическом использовании! Но как?

Эта попытка закончилась без графических ошибок и с мощным повышением производительности - но только на картах nVidia/AMD. Графические процессоры Intel не могли справиться с этим, экран мигал белым.

Итак, я закончил настройку и правильно использовал BufferStrategy. Класс Game теперь расширяет Canvas, а не JPanel, как получение графики из JFrame, а использование этого для рисования на JPanel заканчивается смещением, поскольку оно рисуется под заголовком. Все еще быстро, исправьте 60 FPS.

Теперь, когда я создал BufferStrategy в JFrame (Основной класс), изображения вообще не было. Я исправил это, настроив BufferStrategy в классе Game (Canvas). Теперь картина правильная, но сама игра медленна, как улитка. Один взрыв срывает FPS до ~ 10, но только на nVidia/AMD. Иронический. Даже старые графические процессоры Intel обрабатывают его с 60 FPS, мне удалось получить 10000 частиц в движении при 60 FPS на интегрированном Intel GPU на 5-6 лет. Моя карта ударяет до 100% нагрузки при взрыве.

Вот мой основной код (весь код нечеткий и длинный):

public class Game extends Canvas {
 -snip-
 public void tick() {
    BufferStrategy bf = getBufferStrategy();
    Graphics g = null;
    try {
        g = bf.getDrawGraphics();
        paint(g);
    } finally {
        g.dispose();
    }
    if (!bf.contentsLost()) {
        bf.show();
    } else {
        System.err.println("Buffer lost!");
    }
    Toolkit.getDefaultToolkit().sync();
 }
 public void setBuffers() {
    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gs = ge.getDefaultScreenDevice();
    GraphicsConfiguration gc = gs.getDefaultConfiguration();

    if (gc.getBufferCapabilities().isMultiBufferAvailable()) {
        createBufferStrategy(3);
        System.out.println("Triple buffering active");
    } else {
        createBufferStrategy(2);
        System.err.println("Triple buffering not supported by the GPU");
        System.out.println("Double buffering active");
    }
    System.out.println("FullScreen required: " + getBufferStrategy().getCapabilities().isFullScreenRequired());
    System.out.println("Page flipping: " + getBufferStrategy().getCapabilities().isPageFlipping());
 }
 public void paint(Graphics g) {
    super.paint(g);
    //set up RenderingHints, draw stuff
 }
 -snip snip-
}

Конечно, я вызываю setBuffers(), как только начинается Игра.

Надеюсь, вы можете мне помочь, эта проблема очень важна, поскольку использование VolatileImage, на мой взгляд, не даст повышения производительности, так как манипулирование изображениями нужно делать с использованием BufferedImage. Бьюсь об заклад, я пропускаю что-то тривиальное или что-то не так.

Вот мои спецификации компьютера, просто чтобы показать это не аппаратную проблему: Intel Core i7-3770k @4.3GHz, nVidia GTX 460, 12 ГБ RAM

"Быстрый" компьютер: Intel Core 2 Duo 2,7 ГГц, Интегрированная графика Intel, 2 ГБ RAM

Спасибо за помощь и время!:)

ИЗМЕНИТЬ Может ли помочь VolatileImage? Если я правильно знаю, манипуляции с изображениями должны выполняться с использованием BufferedImages, но их рисование вяло.

4b9b3361

Ответ 1

Вот несколько вещей, которые нужно проверить:


Не зная вывод консоли/ошибки вашей функции setBuffers, это сложно сказать. Является ли Intel использующим createBufferStrategy (2); в то время как NV использует createBufferStrategy (3);? если это может быть частью проблемы с ним с использованием дополнительной памяти.


Вы уже пробовали java2d System.properties? http://docs.oracle.com/javase/1.5.0/docs/guide/2d/flags.html Особенно свойство трассировки для отладки.

Только для Windows

System.setProperty("sun.java2d.transaccel", "True");
System.setProperty("sun.java2d.d3d", "True");
System.setProperty("sun.java2d.ddforcevram", "True");

Все платформы

System.setProperty("sun.java2d.opengl", "True");

Для отладки

System.setProperty("sun.java2d.trace", "timestamp,log,count");
//// -Dsun.java2d.trace=[log[,timestamp]],[count],[out:<filename>],[help],[verbose]

Toolkit.getDefaultToolkit() синхронизации(). фактически не заставляет монитор VSync работать с BufferCapabilities (в вашей функции setBuffers).

    BufferStrategy bf = getBufferStrategy();
    if (bf != null) {
        BufferCapabilities caps = bf.getCapabilities();
        try {
            Class ebcClass = Class.forName(
                "sun.java2d.pipe.hw.ExtendedBufferCapabilities");
            Class vstClass = Class.forName(
                "sun.java2d.pipe.hw.ExtendedBufferCapabilities$VSyncType");

            Constructor ebcConstructor = ebcClass.getConstructor(
                new Class[] { BufferCapabilities.class, vstClass });
            Object vSyncType = vstClass.getField("VSYNC_ON").get(null);

            BufferCapabilities newCaps = (BufferCapabilities)ebcConstructor.newInstance(
                new Object[] { caps, vSyncType });

            createBufferStrategy(2, newCaps);

            // TODO: if success, setCanChangeRefreshRate(false) and setRefreshRate(60). 
            // Possibly override refreshRateSync()?
        }
        catch (Throwable t) {
            // Ignore
            t.printStackTrace();
        }
    }

EDIT Этот код был из (http://pulpcore.googlecode.com/hg-history/3c4001969922b93048e0a166395115df059a2059/src/pulpcore/platform/applet/BufferStrategySurface.java)

Кроме того, вы должны вызывать setBuffers ПОСЛЕ выполнения конструктора Canvas, и он был добавлен в JFrame.


Я "относительно уверен", вам не нужно называть super.paint(g); Я не знаю, связано ли это с вашей конкретной проблемой, но вы должны удалить строку из своей функции рисования.


Несмотря на то, что вы не показываете код, ваш механизм взрыва с использованием 200 случайных движущихся спрайтов может быть основной причиной ошибок. Объясните максимальное количество взрывов, которые вы хотите иметь на экране за один раз, и сгенерируйте эти спрайты N * 200 перед раздачей, поместите их в список массивов ArrayList (N), а затем в своей ссылке на них циклически. Explosionclass= взрывList.get(currentExplosionIndex% explosionList.size());

Ответ 2

Проверьте размер изображений по сравнению с кэшами и попробуйте диагностировать количество промахов в кеше. Читайте на ориентированном на данные дизайне, это обычно является частью этого.

Альтернативой является изменение способа, которым вы делаете взрывы, таким образом, чтобы вы не загружали 200 спрайтов и не распространяли их (вы их повторно используете или загружаете их каждый раз, когда вы совершаете взрыв?). Создание анимированного взрыва требует меньше энергии, хотя это может быть не так эффектно, и вам нужно будет немного оживить.

Java также не самый оптимальный язык для создания игр. Вы работаете на очень высоком уровне, и JVM немного замедляет работу. У моих друзей были схожие проблемы при создании небольших игр, было сложно сделать игру, которая не отставала.

Ответ 3

Когда вы отправляете вещи на GPU, GPU считывает информацию с GDRAM. Если вы читаете данные в памяти, вызывая getDrawGraphics, тогда память, вероятно, считывается с графической карты обратно в ОЗУ. CPU может получить доступ только к RAM (DRAM), графические процессоры могут получить доступ только к GDRAM. С встроенными графическими процессорами это, однако, отличается, так как у них нет собственной ОЗУ, они используют часть обычной ОЗУ.

Чтобы вернуться, чтобы ваш код работал быстро, определите, какое оборудование у вас есть, затем соответствующим образом вызовите соответствующий метод (тот, который был до изменения вашего кода или после него).