I writing a simple raytracer plugin for a software that generates the pixels from a multithreaded function. the pseudo code is
Scene* scene;
void engine_(int y, int, x, pixel& out){ // this function is threaded for each orizontal line y
Buffer* line;
my_render_engine(y, scene, buffered_line); // here is where I'm accessing a lot of shared data, incurring into data racing
out = *buffered_line; // here I have just a line of pixels
}
My Scene* contains many shared variables (camera, samplers, shaders, etc) and the renderer only need read access to them. These have been generated before the engine_ is called. Accessing them concurrently creates undefined behaviours.
I have grabbed a thread_pool template trying to protect my threads like this
void engine\_(int y, int, x, pixel& out){ // this function is threaded for each orizontal line y
m.lock()
Buffer\* line(width);
auto render_line = \[this\](int y, int x, Buffer\* buffer) {
Scene thread_scene = Scene();
... creating scene ...
}
thread_world.camera_ptr->my_render_engine(y, x, r, thread_scene , buffer_image_ptr);
};
//for (int j = 0; j < height; j++)
m_threadPool.AddTask(render_line, j, x, r, buffer_image_ptr); // running render function for each
//thread safely
m.unlock()
out = *buffered_image; // here I have my entire image
}
this kinda works or at least I'm able to protect my variables. it's very inefficient in many ways and needs improvements, like generating just the right amount of scene as my number of cores. The big problem is that I cannot visualize what I'm doing unless I wait to generate the entire image, which I cannot do. The engine_ function let me visualize each row of pixels as soon as it is generated, but I have to let it do it. Using my thread pool as it is doesn't work obviously, because I'm overriding engine_ in easy words.
A temporary solution is to create a brand new scene everytime I run the renderer like this
void engine_(int y, int, x, pixel& out){ // this function is threaded for each orizontal line y
Scene* thread_scene; .....creating scene..... Buffer* line(width);
my_render_engine(y, 3dscene, buffered_line); // here is where I'm accessing a lot of shared data, incurring into data racing
out = *buffered_line; // here I have just a line of pixels
}
it is similar to the thread_pool but it let me visualize the image I'm generating line by line. The problem is that is not well thread protected. inside the scene, there are shaders and 3dsamplers that are shared between many 3d objects like cameras, lights and geometries.
So here is where I need help:
I 'think' that when I'm creating my scene, the pointers of the variables inside it are shared between the threads and when the threads access them concurrently the program crashes:
class Scene{
Camera* cam;
Sampler* sampler;
Object_list<object*> objs;
Tracer* tracer_ptr;
Shader_list<shader*> shaders
}; // these pointers refer to the same stuff
My questions are: ->Am I right? even though I'm creating several copies of the scenes I'm not protecting their variables? -> if that's the case, how can I make these variables protected? hopefully I don't need to copy them (especially the mesh_ptr which are quite heavy.
I've managed to solve the issue by creating a map containing the needed data for each thread and a thread::id. the data is the World class which contains pointers for the shared read-access data and a copy for what is not (hopefully is thread safe, but I'm not entirely sure).