簡體   English   中英

以阻塞的方式在OpenGL中渲染紋理(視頻),因此在渲染時我接收到的視頻幀不會被新的替換

[英]Rendering texture (video) in OpenGL in a blocking way so my received video frame won't get replaced by new one while rendering

我認為不需要此代碼的GTK細節即可了解正在發生的事情。 glDraw()函數從Frame frame執行OpenGL渲染,該Frame frame是從decodedFramesFifo -safe雙端隊列decodedFramesFifo檢索的。

.h文件

class OpenGLArea2 : public Gtk::Window
{
public:
    OpenGLArea2();
    ~OpenGLArea2() override;

public:
    Gtk::Box m_VBox{Gtk::ORIENTATION_VERTICAL, false};
    Gtk::GLArea glArea;

    virtual bool render(const Glib::RefPtr<Gdk::GLContext> &context){};
};

然后是cpp文件:

OpenGLArea2::OpenGLArea2()
{
    set_default_size(640, 360);

    add(m_VBox);

    glArea.set_hexpand(true);
    glArea.set_vexpand(true);
    glArea.set_auto_render(true);
    m_VBox.add(glArea);

    glArea.signal_render().connect(sigc::mem_fun(*this, &OpenGLArea2::render), false);

    glArea.show();

    m_VBox.show();
}

glFlush

bool OpenGLArea2::render(const Glib::RefPtr<Gdk::GLContext> &context)
{
    try
    {
        glArea.throw_if_error();

glFlush glDraw();

        glFlush();
    }
    catch (const Gdk::GLError &gle)
    {
        std::cerr << "An error occurred in the render callback of the GLArea" << std::endl;
        return false;glFlush
    }
}

void OpenGLArea2::run()
{
    while (true)
    {
        //Important: if decodedFramesFifo does not have any data, it blocks until it has
        Frame frame = decodedFramesFifo->pop_front();
        this->frame = std::move(frame);
        if (!firstFrameReceived)
            firstFrameReceived = true;
        queue_draw();
    }
}

這是glDraw()的示意圖:

void OpenGLArea2::run()
{
    //Creates shader programs
    //Generate buffers and pixel buffer object to render from
    glBufferData(GL_PIXEL_UNPACK_BUFFER, textureSize, frame.buffer(j), GL_STREAM_DRAW);
    //calls glTexSubImage2D to do rendering
}

問題是我有時會遇到細分錯誤。 我嘗試使用gdb和valgrind進行調試,但是在gdb中,它不會顯示調用堆棧,而僅顯示發生錯誤的地方(有些記憶怪異的東西),而在valgrind中,它會將應用程序的速度減慢到1 fps,而根本不會遇到分段錯誤,因為我認為在新數據到達之前有足夠的時間渲染數據。

我懷疑queue_draw()沒有阻塞,因此,它只是標記要渲染的窗口並返回。 然后,窗口調用render() 如果render()足夠快,可以在while循環上到達新幀之前進行渲染,則不會發生數據爭用。 但是,如果render()花費更多的時間,則會到達一個新frame並將其寫入在渲染中間的舊幀的位置

所以問題是 :如何以阻塞方式渲染? 也就是說,不是直接調用queue_draw() ,而是直接調用glDraw()並等待其返回? 我是否可以相信glBufferData()glTexSubImage2D()都以阻塞的方式使用frame數據,而不是簡單地將其標記為稍后發送給GPU?

ps:我發現無效的Gtk :: GLArea :: queue_render無效的Gtk :: GLArea :: set_auto_render(bool auto_render = true),但我認為queue_render()也立即返回。

更新:

有人說我應該使用glFinish 問題在於,在渲染器循環中, queue_draw()立即返回,因此該線程根本沒有被阻塞。 如何不使用queue_draw()進行渲染?

更新:

我在while循環的開頭添加了:

std::unique_lock<std::mutex> lock{mutex};

在while循環的結尾:

conditionVariable.wait(lock);

現在我的渲染函數是這樣的:

    glArea.throw_if_error();

    glDraw();

    glFinish();

    conditionVariable.notify_one();

conidition變量使while循環在渲染完成之前等待,因此它可以安全地刪除接收到的幀(因為它超出范圍)。 但是我仍然收到段錯誤。 我在某些行中添加了日志記錄,發現等待期間發生了段錯誤。 可能是什么原因?

我認為這是最有問題的部分:

Frame frame = decodedFramesFifo->pop_front();
this->frame = std::move(frame);

由於pop_front阻塞並等待幀。 如果在渲染之前得到第二幀,則第一幀將被破壞:

this->frame = std::move(frame); // this->frame now contains second frame, first frame is destroyed


您應該鎖定對此->框架的訪問

void ...::run()
{
    while (true)
    {
        Frame frame = decodedFramesFifo->pop_front();
        std::unique_lock<std::mutex> lk{mutex};
        this->frame = std::move(frame);
        lk.unlock();
        if (!firstFrameReceived)
            firstFrameReceived = true;
        queue_draw();
    }
}

void ...::render()
{
    std::unique_lock<std::mutex> lk{mutex};

    // draw 'this->frame'
}


如果可以std :: move移出幀,並且每幀最大只能渲染一次,則可以:

void ...::render()
{
    std::unique_lock<std::mutex> lk{mutex};
    Frame frame = std::move(this->frame);
    lk.unlock();

    // draw 'frame'
}

暫無
暫無

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

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