Многопоточность RayTracer генерирует артефакты с двумя разными алгоритмами - PullRequest
0 голосов
/ 18 мая 2018

Я пытался реализовать многопоточность с помощью моего RayTracer, но я экспериментирую со странными артефактами, связанными с MT. Теперь я уверен, что это не связано с алгоритмом, потому что это происходит с двумя разными подходами.

Сильфон - это то, что происходит с алгоритмом MT, где первый поток заботится о нижней половине изображения, а другой - о верхней половине. В первом случае я отправляю thread0 для возврата, прежде чем что-либо делать, во втором случае thread1 возвращает.

half-screen-thread0.jpg

half-screen-thread1.jpg

Работает как положено. Теперь оба потока работают вместе:

half-screen-thread0-1.jpg

Давайте перейдем ко второму алгоритму, который использует пиксельный подход, нить 0 -> pixel0, нить 1 -> pixel1, нить 0 -> pixel2, нить 0 -> pixel3.

Первое изображение, когда работает только thread0, второе изображение, когда работает только thread1:

pixel-intercalated-thread00.jpg

pixel-intercalated-thread11.jpg

Еще раз, он работает, как и ожидалось, с четкими сильными черными рядами из-за того, что работает только один поток (он может быть не виден на онлайн-хостинге, но может быть легко виден из автономной программы просмотра изображений)

Теперь оба работают вместе:

pixel-intercalated-thread0-1.jpg

Очень похоже на ранее.

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

BOTO x: 403 y 405 thread_id: 1

BOTO x: 403 y 405 thread_id: 1

BOTO x: 403 y 405 thread_id: 1

BOTO x: 403 y 405 thread_id: 1

Только один из них должен быть напечатан, как в случае ST. И что еще более странно, это то, что это случайно. Если я запускаю версию MT в другой раз, это повторение, скорее всего, исчезнет, ​​но будет отображаться на других пикселях экрана. Другая информация также изменяется, когда эта информация о печати повторяется:

>

 normal.x: 0 normal.y: 0 normal.z: -1.06 x: 400 y 409 thread_id: 0
intersection.x : -275.817 intersection.y: 61.5662 intersection.z: 283.019 
x: 400 y 409 thread_id: 0
green: 0 x: 400 y 409    
BOTO x: 400 y 409 thread_id: 0 
blue: 0 x: 400 y 409 

normal.x: 0 normal.y: 0 normal.z: -1.06 x: 400 y 409 thread_id: 0
intersection.x : -330.647 intersection.y: 73.8051 intersection.z: -283.019  
x: 400 y 409 thread_id: 0
green: 1.23263 x: 400 y 409
blue: 1.86762 x: 400 y 409
BOTO x: 400 y 409 thread_id: 0 

normal.x: 0 normal.y: 0 normal.z: 1.06 x: 400 y 409 thread_id: 0
intersection.x : -374.61 intersection.y: 83.6182 intersection.z: 283.019  
x: 400 y 409 thread_id: 0
green: 1.36558 x: 400 y 409
blue: 2.06906 x: 400 y 409
BOTO x: 400 y 409 thread_id: 0

normal.x: 0 normal.y: 0 normal.z: -1.06 x: 400 y 409 thread_id: 0
intersection.x : -409.859 intersection.y: 91.4863 intersection.z: -283.019 x: 400 y 409 thread_id: 0
x: 400 y 409 thread_id: 0
green: 1.36558 x: 400 y 409
blue: 2.06906 x: 400 y 409
BOTO x: 400 y 409 thread_id: 0

Я хотел бы, чтобы любые предложения попытались выяснить, что происходит. Если кто-то заинтересован в коде: Некоторый код (изменения, необходимые для реализации алгоритма чередования пикселей):

void RayTracer::draw_multithread (Image *image, Scene *scene, int thread_id) {
  int ctr=0; //initial position of the array to write the color values
  int initial_x=0; //initial pixel
  if (thread_id==1) {
    initial_x=1;
    ctr=1;
  }

  if (thread_id==0) //to test one thread each time
    return;        

  for (int y=0; y<image->getHeight(); y++) {
    //float px = px_inicial;
    //py = py + ystep;
    for (int x=initial_x; x<image->getWidth(); x+=2) { //+2 to alternate the pixels between threads
      ....
      ....
      ....
      b [ctr] = min (blue*255.0f,255.0f); //separate arrays for each color decided to use three temporarily to easy the MT implementation
      c [ctr] = min (green*255.0f,255.0f);
      d [ctr] = min (red*255.0f,255.0f);
      ctr+=2;   //increment in two due to MT
    }
  }
pthread_exit (NULL);    

Некоторый другой код (изменения, необходимые для реализации половины экрана для каждого алгоритма потока):

void RayTracer::draw_multithread2 (Image *image, Scene *scene, int thread_id) {
   float inv_div=0.5; //control how much work is made by each thread
   int ctr=0; //thread 0 starts on the beggining
   int y=0;   //same as above
   int initial_x=0;
   if (thread_id==1) {
     cout << "thread_1" << endl;
     ctr=1024*384; //thread 1 starts on the half
     y=384; // same as above
     inv_div=1; //necessary for thrad1 to do half of the rendering
   }

   if (thread_id==0)
      return;

   for (; y<(float)image->getHeight()*inv_div; y++) { //increment in one
     //float px = px_inicial;
     //py = py + ystep;
     for (int x=initial_x; x<image->getWidth(); x++) {
     b [ctr] = min (blue*255.0f,255.0f);
     c [ctr] = min (green*255.0f,255.0f);
     d [ctr] = min (red*255.0f,255.0f);
     ctr++; // also increments in one pixel each iteration
   }
 }
 pthread_exit (NULL);

Код установки MT:

 pthread_t threads [2];
 thread_data td_array [2]; //for being able to throw the MT methods on class
 void *status;
 TGAManager tgaManager ("z.tga",true);
 if (tgaManager.isFileOpen()) {
   tgaManager.writeHeadersData (image);
   RayTracer rt (image.getHeight() * image.getWidth(),1);
   int rc;
   for (int i=0; i<2; i++) {
     //cout << "main() : creating thread, " << i << endl;
     td_array[i].thread_id=i; //thread identification to make possible the methods working differently according with the calling thread
     td_array[i].rt_ptr = &rt; //to pass the same RayTracer instancy to each method, since the array where the pixels colors are written is there
     td_array[i].img_ptr = &image;
     td_array[i].scene_ptr = &scene;
     //cout << "td_array.thread_index: " << td_array[i].thread_id << endl;
     rc = pthread_create (&threads[i], NULL, RayTracer::run_thread, &td_array[i]);
  }
  if (rc) {
     cout << "Error:unable to create thread," << rc << endl;
     exit(-1);
  }
  for (int i=0; i<2; i++ ) {
     rc = pthread_join(threads[i], &status);
     if (rc) {
        cout << "Error:unable to join," << rc << endl;
        exit(-1);
     }
  }


struct thread_data { // struct to pass all the required information for being able to let the MT methods on the RayTracer class
  int thread_id;
  RayTracer* rt_ptr;
  Image* img_ptr;
  Scene* scene_ptr;
};   

static void* run_thread (void* ptr) { //calls the real multithreading method draw_multithread2 or draw_multithread 
     cout << "run_thread..." << endl;
     void *nullptr;
     thread_data* td = static_cast <thread_data*> (ptr);
     td->rt_ptr->draw_multithread2 (td->img_ptr,td->scene_ptr,td->thread_id);
     //td->rt_ptr->draw (td->img_ptr,td->scene_ptr);
     return nullptr;
     cout << "" << endl;
  }

Полная ссылка на многопоточные методы трассировки лучей: https://gitlab.com/asmf2/asmfrt/blob/master/RayTracer.cpp К настройке МТ

https://gitlab.com/asmf2/asmfrt/blob/master/main.cpp https://gitlab.com/asmf2/asmfrt/blob/master/RayTracer.h

...