Archive

Archive for February, 2014

pbrt: ReleaseSemaphore Error 298 on Windows

February 8th, 2014 No comments

I love the pbrt raytracer and the accompanying book. Unfortunately, the latest version has a habit of crashing when trying to render large images on Windows.

The error message

Error: Error from ReleaseSemaphore: 298

leads to the src/core/parallel.cpp file, which has a Semaphore implementation using the native operating system semaphores on Windows. Querying Visual Studio’s handy Error Lookup Tool for the error code 298 reveals the nature of the error:

Too many posts were made to a semaphore. 

Bummer.

Looking closely at the CreateSemaphore function in the Windows API, we notice the lMaximumCount parameter, which specifies how high the semaphore can count. The implementation of pbrt currently sets this to a default of 65535 which does not seem to enough for large images. But what would be a reasonable default here? Are there any performance drawbacks if we make this number too high? Why do I even have to think about a maximum at all…

Fortunately, with C++11’s library we can easily roll our own Semaphore implementation that does not suffer from this limitation. If your compiler does not ship with a working thread library yet, the following fix works just as well with Boost.Thread (simply use namespace boost instead of std).

The interface of pbrt’s Semaphore is simple: Whenever a resource is produced, the Producer thread calls Post, optionally specifying the number of resources produced. A Consumer can call Wait or TryWait to consume an existing resource. The former will block in case no resources are available, while the latter always returns immediately, indicating through its return value whether a resource was consumed or not.

[src/core/parallel.h]
#include <mutex>
#include <condition_variable>
 
[...]
 
class Semaphore {
public:
    // Semaphore Public Methods
    Semaphore();
    ~Semaphore();
    void Post(int count = 1);
    void Wait();
    bool TryWait();
private:
    std::mutex m_mutex;
    std::condition_variable m_cond;
    int m_count;
};

Instead of an operating system semaphore, we roll our own counter. I use a simple int here, which worked fine for my ~2M pixel renderings, but if you are paranoid about overflows, feel free to crank that up to a uint64_t. We use a mutex for protecting the counter from concurrent access and throw in a condition variable for waiting.

The implementation is the typical usage pattern of a condition variable: The condition in our case is the availability of a resource, represented by a counter. We start the counter with 0; calls to Post increment it, while calls to Wait decrement it:

[src/core/parallel.c]
 
Semaphore::Semaphore()
    :m_count(0)
{
}
 
Semaphore::~Semaphore()
{
}
 
void Semaphore::Post(int count)
{
    std::lock_guard<std::mutex> lk(m_mutex);
    m_count += count;
    for (int i=0; i<count; ++i) {
        m_cond.notify_one();
    }
}
 
void Semaphore::Wait()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    m_cond.wait(lk, [this]() -> bool { return m_count > 0; } );
    --m_count;
}
 
bool Semaphore::TryWait()
{
    std::unique_lock<std::mutex> lk(m_mutex);
    bool const success = m_cond.wait_for(lk, std::chrono::seconds(0),
        [this]() -> bool { return m_count > 0; });
    if (success) {
        --m_count;
    }
    return success;
}

Fortunately, the original implementation for Semaphore was not very robust, so this is fairly straightforward. The counter can still overflow, so feel free to add an assert here if that bothers you. Also note that destroying the Semaphore while there are still unnotified Consumers blocked on the condition variable is not a good idea.

There is one minor thing left to make this compile: pbrt defines a number of fixed-width integer types, which, at least on Visual Studio, clash with the ones from the standard library that get pulled in via the thread headers. This can be easily fixed by throwing out the respective typedefs in src/core/pbrt.h and replacing them with the ones from <cstdint>.

With all of that in place I can finally render the Buddha in 3000×6000 again. Well worth the hassle 😀

Categories: Programming