代码之家  ›  专栏  ›  技术社区  ›  shmoo6000

在程序结束前不执行所有写操作

  •  1
  • shmoo6000  · 技术社区  · 6 年前

    在学校的一个项目中,我们的任务是写一个光线跟踪器。我选择使用C++,因为它是我最喜欢的语言,但是我得到了一些奇怪的东西。

    请记住,我们还处于本课程的前几节课,所以现在我们仅限于检查光线是否击中某个物体。

    当我的光线跟踪器很快完成(不到1秒的时间用于实际的光线跟踪)我注意到并不是所有的点击都在我的“帧缓冲区”中注册。

    为了举例说明,这里有两个例子:

    First Image 2.: Second Image

    我想知道有没有人能帮我弄清楚为什么会这样?

    我应该提到我的应用程序是多线程的,代码的多线程部分如下所示:

    Stats RayTracer::runParallel(const std::vector<Math::ivec2>& pixelList, const Math::vec3& eyePos, const Math::vec3& screenCenter, long numThreads) noexcept
    {
        //...
    
        for (int i = 0; i < threads.size(); i++)
        {
            threads[i] = std::thread(&RayTracer::run, this, splitPixels[i], eyePos, screenCenter);
        }
    
        for (std::thread& thread: threads)
        {
            thread.join();
        }
    
        //...
    }
    

    Stats RayTracer::run(const std::vector<Math::ivec2>& pixelList, const Math::vec3& eyePos, const Math::vec3& screenCenter) noexcept
    {
        this->frameBuffer.clear(RayTracer::CLEAR_COLOUR);
    
        // ...
    
        for (const Math::ivec2& pixel : pixelList)
        {
            // ...
    
            for (const std::shared_ptr<Objects::Object>& object : this->objects)
            {
                std::optional<Objects::Hit> hit = object->hit(ray, pixelPos);
    
                if (hit)
                {
                    // ...
    
                    if (dist < minDist)
                    {
                        std::lock_guard lock (this->frameBufferMutex);
    
                        // ...
                        this->frameBuffer(pixel.y, pixel.x) = hit->getColor();
                    }
                }
            }
        }
    
        // ...
    }
    

    这是framebuffer类的运算符()

    class FrameBuffer
    {
        private:
            PixelBuffer buffer;
    
        public:
            // ...
    
            Color& FrameBuffer::operator()(int row, int col) noexcept
            {
                return this->buffer(row, col);
            }
    
            // ...
    }
    

    使用PixelBuffer的操作符()

    class PixelBuffer
    {
        private:
            int mRows;
            int mCols;
    
            Color* mBuffer;
    
        public:
            // ...
    
            Color& PixelBuffer::operator()(int row, int col) noexcept
            {
                return this->mBuffer[this->flattenIndex(row, col)];
            }
    
            // ...
    }
    

    我没有费心使用任何同步原语,因为每个线程都会从完整图像中指定一个特定的像素子集。线程为每个指定的像素投射一条光线,并将结果颜色写回该像素槽中的颜色缓冲区。这意味着,当我的所有线程同时访问(并写入)同一对象时,它们不会写入相同的内存位置。

    应该注意的是,我在线程之间划分像素的方式决定了伪影的方向。如果我给每个线程一组行,工件将是水平线,如果我给每个线程一组列,工件将是垂直线。

    建议之后 Jeremy Friesner 我在一个线程上运行了10次raytracer,没有任何问题,所以问题确实看起来是一个争用条件。

    1 回复  |  直到 6 年前
        1
  •  1
  •   shmoo6000    6 年前

    正如您在代码中看到的,每个线程分别调用framebuffer.clear()(不锁定mutex!)。这意味着线程A可能已经达到5-10像素,因为它是在线程B清除帧缓冲区时首先启动的。这将擦除线程A已经命中的像素。

    通过将framebuffer.clear()调用移到runParallel()方法的开头,我能够解决这个问题。