Multithreading RayTracer generating artifacts with two different algorithms

穿精又带淫゛_ 提交于 2020-01-06 14:26:07

问题


I have been trying to implement multithreading with my RayTracer, but I am experimenting strange artifacts related to MT. Now I am sure that is not algorithm related, because its happening with two different approaches.

Bellow is what happening with a MT algorithm where the first thread takes care of the lower half of the image and the other the top half. In first case I send the thread0 to return before doing anything, in the second case, the thread1 returns.

Working as expected. Now with both threads working together:

Let's move to the second algorithm, that takes a pixel by pixel approach, thread 0 -> pixel0, thread 1 -> pixel1, thread 0 -> pixel2, thread 0 -> pixel3.

First image is when only thread0 works,second image when only thread1 operates:

Once more, it's working as expected, with clear strong black rows due to fact that only one thread is working (it may not be visible from online hosting, but can easily be seen from a offline image viewer)

Now, both working together:

Very similar to before.

When running the program I was able to identify that the calculation of some pixels seems to be happening more than once: Look at this console output:

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

Only one of these should be printed, as in the ST case. And what's even stranger, is that this is random. If I run the MT version other time, this repetition is likely to be gone, but will show up on another screen pixels. Other information also vary when these print information repeats:

>

 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

I would like any suggestions to try to figure out what's happening. If someone is interested in the code: Some code (changes necessary to implement the pixel alternating algorithm):

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);    

Some other code (changes necessary to implement the half screen to each thread algorithm):

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 setup code:

 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;
  }

The full link to the multithreaded ray-tracing methods: https://gitlab.com/asmf2/asmfrt/blob/master/RayTracer.cpp To the MT setup

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

来源:https://stackoverflow.com/questions/50401206/multithreading-raytracer-generating-artifacts-with-two-different-algorithms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!