OpenGL - 在 ubuntu 的线程上创建 vbo?

Posted

技术标签:

【中文标题】OpenGL - 在 ubuntu 的线程上创建 vbo?【英文标题】:OpenGL - Creating a vbo on a thread in ubuntu? 【发布时间】:2018-05-08 14:39:05 【问题描述】:

我有一个应用程序,其中服务器上有远程网格数据。其中一些网格有 400k 个顶点,因此在下载过程中需要线程。在一个线程上运行所有内容时我没有任何问题,并且所有代码都工作正常,除非我将下载的缓冲区上传到线程版本中的 openGL 土地。尝试使用 opengl 调用创建 vbo 时,我最终崩溃了。应用程序通常会在 glGenBuffers 上出现段错误。我在尝试使 OpenGL 调用线程安全时阅读的一些示例涉及特定的 windows api 和平台特定的东西,这在 x11 ubuntu 环境中没有多大帮助。

所以我希望以前做过这件事的人可以指点我打几个电话,这样在重新设计一个工作部件之前这可能是可行的。

我的加载代码非常简单轻量级,vbo上传是glGenBuffer、glBind、glBufferData调用的典型步骤:

///
/// \brief The LoadMeshThread class
///     Thread to load a mesh (geometry data) from database
///
class LoadMeshThread : public vtrus::core::Thread

    CLASSEXTENDS(LoadMeshThread, vtrus::core::Thread)
    ADD_TO_CLASS_MAP
public:
    VTRUS_HOST
    LoadMeshThread( Chunk* chunk, uint32_t mapID, ChunkGrid* chunkVolume ) :
      super(),
      ChunkToLoadMesh(chunk),
      CpuVertices(NULL),
      CpuNormals(NULL),
      VertexCount(0),
      ChunkVolume(chunkVolume)
    
        printf("Loading [%s]\n", chunk->GridLocation.ToString().str());
        MapID = mapID;

        ChunkToLoadMesh->AddRef();
        ChunkGrid::LiveLoadThreads++;
    

    ///
    /// \brief ~LoadHashThread
    /// Destroyed after pthread has ended
    virtual ~LoadMeshThread()
    
        vprintf(vtrus::debug::Threading, "[%d]\n", this->ThreadID);
        ChunkToLoadMesh->Release();
        ChunkGrid::LiveLoadThreads--;
        delete [] CpuVertices;
        delete [] CpuNormals;
    

    ///
    /// \brief Run
    ///  Called by base class when the pthread is created
    virtual void Run();

    ///
    /// \brief ReadFromDataBase
    ///
    void ReadFromDataBase();

private:
    ChunkGrid* ChunkVolume; //Access to world data
    uint32_t MapID;
    vtrus::slam::Chunk* ChunkToLoadMesh;
    GLfloat* CpuVertices;
    GLfloat* CpuNormals;
    int VertexCount;
;

///
/// \brief LoadMeshThread::ReadFromDataBase
/// Calls into mysql and grabs the blobs containing the data
void LoadMeshThread::ReadFromDataBase()

    Eigen::Vector3i chunkR3 = ChunkToLoadMesh->GridLocation.ToEigen();
    uint32_t chunkID = R3ToChunkID( chunkR3 );
    vtrus::database::DataBase* vtrusDB = vtrus::database::DataBase::GetInstance();

    
        vtrusDB->Driver->threadInit();
        
            try
            
                vtrus::core::String connectionString = vtrusDB->GetConnectionString();
                sql::Connection* connection = vtrusDB->Driver->connect(connectionString.str(), "gobble", "gobblegobble" );
                connection->setSchema( "gobble" );

                sql::ResultSet* result = vtrusDB->GetMeshData( connection, chunkID, MapID, 1 );

                if( result != NULL && result->next() )
                
                    std::istream* verticesBlob = result->getBlob("VertexBuffer");
                    std::istream* normalsBlob = result->getBlob("NormalsBuffer");
                    VertexCount = result->getInt("VertexCount");

                    uint bufferSize = VertexCount*sizeof(vtrus::geometry::Vec3f);
                    CpuVertices = new GLfloat[bufferSize];
                    CpuNormals = new GLfloat[bufferSize];
                    //printf("Loading %d entries \n", entryCount );
                    verticesBlob->read( reinterpret_cast<char*>(&CpuVertices[0]), (std::streamsize)bufferSize );
                    normalsBlob->read( reinterpret_cast<char*>(&CpuNormals[0]), (std::streamsize)bufferSize );

                    delete verticesBlob;
                    delete normalsBlob;
                

                delete result;
                delete connection;
            
            catch ( sql::SQLException * exception )
            
                //do nothing
                printf("WARNING: SqlException on Loading\n");
            
        
        vtrusDB->Driver->threadEnd();
    


void LoadMeshThread::Run()

    vtrus::core::ScopedTimer timer("**** LoadHashThread::Run ****");

    vprintf(vtrus::debug::Threading, "[%d]\n", this->ThreadID);
    int numAttempts = 0;
    const int maxAttenpts = 10;
    uint countRemaining = 0;
    uint maxCount = 0;

    ReadFromDataBase();

    if( VertexCount > 0 )
    
        printf("Creating [%d] vertices\n", VertexCount);
        //CRASH here when the mesh constructor calls glGenBuffer          
        vtrus::resources::Mesh* newMesh = new vtrus::resources::Mesh(VertexCount);
        //upload cpu vertices / normals to GPU

        glBindBuffer( GL_ARRAY_BUFFER, newMesh->GetVertexBuffer()->bo );
        glBufferData(GL_ARRAY_BUFFER, VertexCount * sizeof(vtrus::geometry::Vec3f), &CpuVertices[0], GL_STATIC_DRAW);

        glBindBuffer( GL_ARRAY_BUFFER, newMesh->GetNormalsBuffer()->bo);
        glBufferData(GL_ARRAY_BUFFER, VertexCount * sizeof(vtrus::geometry::Vec3f), &CpuNormals[0], GL_STATIC_DRAW);

        glBindBuffer( GL_ARRAY_BUFFER, 0 );

        ChunkVolume->AddMesh(newMesh, ChunkToLoadMesh);
        newMesh->Release();

        ChunkToLoadMesh->IsLoaded = true;
        ChunkToLoadMesh->IsSaved = true;
        ChunkToLoadMesh->SavedBlockCount = maxCount;
    

    //Access mutex
    ChunkToLoadMesh->IsLoading = false;

【问题讨论】:

有什么阻止您在工作线程中处理网格并将数据指针传递给您的 GL 线程,然后由它调度它? 我想除了需要在工作线程死后从主线程跟踪 cpu 指针。我绝对可以做到这一点,只是一厢情愿地将网格创建作为自己的代码单元我猜 :) 【参考方案1】:

在可以使用任何glXXX 命令之前,必须将 gl-context 设置为将使用这些命令的线程的当前线程。

在 X11 世界中,要使用的命令是 glXMakeContextCurrent 或更早版本,但仍然有效 glXMakeCurrent。见glX doc。如果您正在使用一些为您处理 gl-context 的库,请在其文档中搜索。

您可以在一个 gl-context 中上传数据并在另一个 gl-context 中呈现它。为此,这两个上下文必须是“共享的”,这是您通常在它们的构造函数中执行的操作。 但是,如果您的显卡不能在渲染时读取,那么性能不会有很大的提升。

【讨论】:

以上是关于OpenGL - 在 ubuntu 的线程上创建 vbo?的主要内容,如果未能解决你的问题,请参考以下文章

强制 OpenGL 渲染始终在 Qt 中的相同 (Q) 线程上运行

在 Ubuntu 上设置 OpenGL

(解决追加200分)在ubuntu上怎么安装openGL??

glDrawArrays 仅在原点绘制一个点(在 ubuntu 上带有 glut 的 OpenGL)

使用 ubuntu 在不同的显示器上共享 opengl 上下文

为啥基于 GLX 的应用程序可以在 Ubuntu 上的 Wayland 上运行?