簡體   English   中英

在程序結束之前不執行所有寫入

[英]Not executing all writes before end of program

對於一個學校項目,我們的任務是編寫光線追蹤器。 我選擇使用 C++,因為它是我最熟悉的語言,但我得到了一些奇怪的工件。

請記住,我們仍處於課程的前幾節課,所以現在我們僅限於檢查光線是否擊中某個對象。

當我的光線跟蹤器快速完成時(實際光線跟蹤花費的時間不到 1 秒),我注意到並非所有點擊都在我的“幀緩沖區”中注冊。

為了說明,這里有兩個例子:

1.: 第一張圖片 2.: 第二張圖片

在第一張圖像中,您可以清楚地看到存在水平偽影。 第二張圖像包含垂直偽影。

我想知道是否有人可以幫助我弄清楚為什么會這樣?

我應該提到我的應用程序是多線程的,代碼的多線程部分如下所示:

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();
    }

    //...
}

RayTracer::run 方法按如下方式訪問幀緩沖區:

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 類的 operator()

class FrameBuffer
{
    private:
        PixelBuffer buffer;

    public:
        // ...

        Color& FrameBuffer::operator()(int row, int col) noexcept
        {
            return this->buffer(row, col);
        }

        // ...
}

其中使用了 PixelBuffer 的 operator()

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)];
        }

        // ...
}

我沒有費心使用任何同步原語,因為每個線程都從完整圖像中分配了特定的像素子集。 該線程為其分配的每個像素投射一條光線,並將結果顏色寫回該像素插槽中的顏色緩沖區。 這意味着,雖然我的所有線程都同時訪問(和寫入)同一個對象,但它們不會寫入相同的內存位置。

經過一些初步測試,使用 std::lock_guard 來保護共享幀緩沖區似乎有幫助,但這不是一個完美的解決方案,仍然會出現偽像(盡管不太常見)。

需要注意的是,我在線程之間划分像素的方式決定了偽像的方向。 如果我給每個線程一組行,工件將是水平線,如果我給每個線程一組列,工件將是垂直線。

另一個有趣的結論是,當我追蹤更復雜的對象時(這些需要 30 秒到 2 分鍾之間的任何時間),這些神器非常罕見(到目前為止,我在 100 到 1000 次跟蹤中見過一次)

我不禁覺得這是一個與多線程相關的問題,但我真的不明白為什么 std::lock_guard 不能完全解決問題。

編輯:根據Jeremy Friesner 的建議,我在一個線程上運行了大約 10 次光線追蹤器,沒有任何問題,所以問題確實似乎是一個競爭條件。

感謝 Jeremy Friesner,我解決了這個問題。

正如您在代碼中看到的,每個線程分別調用 framebuffer.clear() (不鎖定互斥鎖!)。 這意味着線程 A 可能已經達到了 5-10 個像素,因為它是在線程 B 清除幀緩沖區時首先啟動的。 這將擦除線程 A 已經命中的像素。

通過將 framebuffer.clear() 調用移動到 runParallel() 方法的開頭,我能夠解決這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM