[英]Not executing all writes before end of program
對於一個學校項目,我們的任務是編寫光線追蹤器。 我選擇使用 C++,因為它是我最熟悉的語言,但我得到了一些奇怪的工件。
請記住,我們仍處於課程的前幾節課,所以現在我們僅限於檢查光線是否擊中某個對象。
當我的光線跟蹤器快速完成時(實際光線跟蹤花費的時間不到 1 秒),我注意到並非所有點擊都在我的“幀緩沖區”中注冊。
為了說明,這里有兩個例子:
在第一張圖像中,您可以清楚地看到存在水平偽影。 第二張圖像包含垂直偽影。
我想知道是否有人可以幫助我弄清楚為什么會這樣?
我應該提到我的應用程序是多線程的,代碼的多線程部分如下所示:
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.