2

So I'm working on a ray-tracer, and in order to reduce rendering time, I used std::async to do pixel calculations independently. I used this tutorial, and everything works great, and indeed I was able to save about 70% in rendering time.

Still, some scenes take a while to render, and I'd like to display some-sort of progress bar. As I'm fairly a newbie to async infra, I'm not quite sure how to do that. I'd like some sort of mechanism to only print the progress from the 'main', calling', thread.

Here the rendering loop - notice the commented lines for progress bar - which obviously should not go there:

Image *image = new Image(width, height);

size_t max = width * height;
size_t cores = std::thread::hardware_concurrency();
volatile atomic<size_t> count(0);
vector<future<void>> future_vector;

while (cores--)
{
    future_vector.push_back(
        std::async(launch::async, [=, &camera, &scene, &count]()
        {
            while (true)
            {
                std::size_t index = count++;
                if (index >= max)
                    break;

                GLuint i = index % width;
                GLuint j = index / width;

                Ray ray = camera.generateRay(i + .5, j - .5);
                vec3 color = recursiveRayTrace(scene, ray, maxDepth);
                image->setPixel(i, j, color);

                // THIS IS BAD
                //std::cout << "Progress: [ "<< setprecision(1) << fixed << (count / (GLfloat)max) * 100.0 << "% ] \r";
                //std::cout.flush();

            }
        }));
}

for (auto& e : future_vector) {
    e.get();
}

return image;

UPDATE: So I did what some answers suggested - here are the results - for future readers. My benchmark for a few scenes was 15 seconds (I took something fast on purpose), when rendering asynchronously. When I used mutex, time was slowed down to 26 seconds. (Still better than 1.32 on single-thread). I also actively waited for one of the working threads, and kept updating the progress every 100mili or so - and render time was 16 seconds. So I'm very pleased with this result, as the printing barely slowed the process.

Thanks, Alon

Alonbs
  • 259
  • 1
  • 11
  • 4
    Use an (atomic) global variable to hold the progress. The async function updates it, and in the main thread poll this value for changes. The main thread displays the value (in any way it feels like) when a change is detected. – Some programmer dude Apr 18 '18 at 10:48
  • As another alternative; rather than `while (true)` - do a known amount of work; and then re-queue more when it's done. This will not only allow you to terminate your worker in a nice way should the user close your app; but also give you notification that some % has been completed. – UKMonkey Apr 18 '18 at 10:55
  • @Someprogrammerdude - If you could please give some code scheme. I know how to do what you suggested using pthreads etc, but I'm not sure how to do this: "the main thread displays the value when a change is detected". references would be appreciated too. – Alonbs Apr 18 '18 at 13:49
  • 1
    Detecting a change is easy: Store the old value (initialized to some suitable default value) and compare against the current value. Do whatever needs to be done if a change is detected, and then make the old value equal to the current value. Repeat. And your main thread is at the moment stuck waiting for the futures to become ready, doing nothing else. It could poll the futures using e.g. [`wait_for`](http://en.cppreference.com/w/cpp/thread/future/wait_for) and then check for the change in the progress. – Some programmer dude Apr 18 '18 at 14:31
  • @Someprogrammerdude thanks! that's what I was looking for. – Alonbs Apr 20 '18 at 12:16

1 Answers1

1

Seems you need a lock while using std::cout , otherwise the async tasks will make a mess output( they all try to print on the console at same time). However I recommend you using GDIplus ( seems you are using in your code) to print the text instead of showing on a console window which is rather ugly.

Image *image = new Image(width, height);

size_t max = width * height;
size_t cores = std::thread::hardware_concurrency();
volatile atomic<size_t> count(0);
vector<future<void>> future_vector;
mutex cout_lock;
while (cores--)
{
    future_vector.push_back(
        std::async(launch::async, [=, &camera, &scene, &count]()
        {
            while (true)
            {
                std::size_t index = count++;
                if (index >= max)
                    break;

                GLuint i = index % width;
                GLuint j = index / width;

                Ray ray = camera.generateRay(i + .5, j - .5);
                vec3 color = recursiveRayTrace(scene, ray, maxDepth);
                image->setPixel(i, j, color);

                {  //lock variable scope
                   lock_guard<mutex> lock(cout_lock)
                   std::cout << "Progress: [ "<< setprecision(1) << fixed << (count / (GLfloat)max) * 100.0 << "% ] \r";
                   std::cout.flush();
                }

            }
        }));
}

for (auto& e : future_vector) {
    e.get();
}

return image;
seccpur
  • 4,996
  • 2
  • 13
  • 21
  • Thanks, but actually I wanted to avoid using mutexs. Something similar to what @someprogrammerdude suggested. But I'll check both options. Thank you. – Alonbs Apr 20 '18 at 10:58