2014-09-27 3 views
0

Итак, я работал над простым raytracer в режиме реального времени, используя чисто Java, и теперь, когда у меня есть большинство вещей, которые работают правильно от рассеянного освещения и зеркальных бликов до луча размышления и преломления. Я начал задаваться вопросом, могу ли я сделать минималистическую приключенческую игру, используя этот свежий двигатель. Единственное, что мешает мне сделать это, - это низкая производительность в разрешениях выше 512x512.Java Raytracer: многопоточный рендеринг ЦП различных частей экрана на поток

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

Вот исходный код моего проекта в GitHub, рендеринг выполняется в Engine.java: https://github.com/Harha/JRay

Я пытался искать ответы относительно этого через Google и из StackOverflow непосредственно не повезло, никто, кажется, действительно сделали что-то подобное публично в прошлом, или тогда я просто плохо разбираюсь в вещах. В любом случае, я не в курсе, и задавался вопросом, может ли кто-нибудь здесь придумать разумный ответ, который можно было бы использовать для синхронизации всех потоков рендеринга с основным потоком, который обновляет растровое изображение.

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

Edit: Вот мой фиксированный цикл запуска.

/* 
* Main run method. 
* Handles the update and render methods. 
*/ 
public void run() { 
    for (int i = 0; i < THREAD_COUNT; i++) { 
     updateDelta(i); 
     lastFPS[i] = getTime(); 
    } 
    requestFocus(); 
    while (running) { 
     updateDelta(0); 
     update(delta[0]); 
     runRenderThreads(); 
     try { 
      e.awaitTermination(10, TimeUnit.MILLISECONDS); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     render(); 
    } 
    stop(); 
} 

Он ждет 10 миллисекунд для визуализации темы, чтобы закончить свою поставленную задачу, если это время будет превышено, то он просто делает изображение в буфер визуализации() и начинает снова и делает, если не заботиться задачи ворс вверх.

ответ

2

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

Я рекомендую вам использовать double buffering. То есть, вы не рисуете непосредственно на экране, а в невидимом буфере и назначаете одинаковое количество пикселей для каждого потока. Вы ждете, пока каждый поток не будет выполнен, а затем замените видимый невидимый буфер и начните с нового изображения. Если вы все еще мерцаете, попробуйте вертикальную синхронизацию.

псевдокод

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 

public class Raytracer 
{ 
    final static int THREADS = 4; 

    public static void main(String[] args) throws InterruptedException 
    { 
     ExecutorService renderPool = Executors.newFixedThreadPool(THREADS); 
     while(true) 
     { 
      for(int i=0;i<THREADS;i++) 
      { 
       renderPool.execute(createRenderThread(i)); 
      } 
      if(renderPool.awaitTermination(1,TimeUnit.MINUTES)) 
      { 
       // delay here in case you want to artifically limit the frame rate 
       // v-sync if necessary here 
       swapBuffers(); // should be available from your graphics library, e.g. open GL 
      } 
     } 

    } 

    private static Runnable createRenderThread(final int i) 
    { 
     return new Runnable() 
     { 
      @Override public void run() 
      { 
       for(int x=..;x<..;x++) 
        for(int y=..;y<..;y++) 
         render(x,y); 
       // do something ... 
      } 
     }; 
    } 

} 
+0

Ну, это было просто ... У меня теперь совершенно гладкий вывод растрового изображения на моем экране, у меня просто не было хорошего представления о том, как я должен проверить, завершил ли исполнитель задание или нет. Спасибо, хотя этот псевдокодовый бит был немного ненужным, но действительно каким-то из вас.Если бы я мог, я бы поднял тебя на колени. – Harha

1

В дополнение к двойной буферизации вы должны убедиться, что вы используете synchronized поменять местами буферы, как только они будут завершены.

Вы можете использовать графическую карту для выполнения рендеринга, но она будет намного быстрее, чем использование процессора.

+0

Спасибо за подсказку. Однако я понятия не имею, как использовать синхронизацию для обмена буферами? Я имею в виду, что в моей ситуации только один поток будет иметь доступ к методу рендеринга, который в конечном итоге заменяет буфер, так как я мог бы извлечь из этого выгоду? Не синхронизировано ли, чтобы в основном предотвратить одновременное выполнение методов несколькими потоками? – Harha

+0

Я хотел убедиться, что потоки рендеринга закончены до того, как вы замените буфер, похоже, что у вас все покрыто. –

+0

(Для чего я не буду постоянно воссоздавать потоки, я бы обработал потоки, работающие в фоновом режиме, и использовал синхронизированные блоки для предотвращения сбоев при обмене данными) –