Alonbs
Alonbs

Reputation: 269

C++ - display progress bar when using std::async

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

Upvotes: 2

Views: 2392

Answers (1)

ObliteratedJillo
ObliteratedJillo

Reputation: 5166

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;

Upvotes: 1

Related Questions