Я пытался реализовать многопоточность с помощью моего RayTracer, но я экспериментирую со странными артефактами, связанными с MT. Теперь я уверен, что это не связано с алгоритмом, потому что это происходит с двумя разными подходами.
Сильфон - это то, что происходит с алгоритмом MT, где первый поток заботится о нижней половине изображения, а другой - о верхней половине. В первом случае я отправляю thread0 для возврата, прежде чем что-либо делать, во втором случае thread1 возвращает.
![half-screen-thread0.jpg](https://s9.postimg.cc/9rooy1qmn/half-screen-thread0.jpg)
![half-screen-thread1.jpg](https://s9.postimg.cc/v1cb8yrin/half-screen-thread1.jpg)
Работает как положено. Теперь оба потока работают вместе:
![half-screen-thread0-1.jpg](https://s9.postimg.cc/pr7ch7wun/half-screen-thread0-1.jpg)
Давайте перейдем ко второму алгоритму, который использует пиксельный подход, нить 0 -> pixel0, нить 1 -> pixel1, нить 0 -> pixel2, нить 0 -> pixel3.
Первое изображение, когда работает только thread0, второе изображение, когда работает только thread1:
![pixel-intercalated-thread00.jpg](https://s9.postimg.cc/b9krat6lr/pixel-intercalated-thread00.jpg)
![pixel-intercalated-thread11.jpg](https://s9.postimg.cc/oqhptpyxr/pixel-intercalated-thread11.jpg)
Еще раз, он работает, как и ожидалось, с четкими сильными черными рядами из-за того, что работает только один поток (он может быть не виден на онлайн-хостинге, но может быть легко виден из автономной программы просмотра изображений)
Теперь оба работают вместе:
![pixel-intercalated-thread0-1.jpg](https://s9.postimg.cc/k5kxdjlzj/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 = ℑ
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