OpenGL - 在 ubuntu 中的线程上创建 vbo?
OpenGL - Creating a vbo on a thread in ubuntu?
我有一个应用程序,其中服务器上有远程网格数据。其中一些网格有 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;
}
在可以使用任何 glXXX
命令之前,必须将 gl-context 设置为将使用这些命令的线程的当前线程。
在 X11 世界中,要使用的命令是 glXMakeContextCurrent
或更早的命令,但仍然有效 glXMakeCurrent
。参见 glX doc。如果您正在使用某些为您处理 gl-context 的库,请在其文档中搜索。
您可以在一个 gl-context 中上传数据并在另一个 gl-context 中呈现它。为此,两个上下文都必须是 "shared",这是您通常在它们的构造函数中执行的操作。
但是,如果您的显卡无法在渲染时进行读取,则不会有很大的性能改进。
我有一个应用程序,其中服务器上有远程网格数据。其中一些网格有 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;
}
在可以使用任何 glXXX
命令之前,必须将 gl-context 设置为将使用这些命令的线程的当前线程。
在 X11 世界中,要使用的命令是 glXMakeContextCurrent
或更早的命令,但仍然有效 glXMakeCurrent
。参见 glX doc。如果您正在使用某些为您处理 gl-context 的库,请在其文档中搜索。
您可以在一个 gl-context 中上传数据并在另一个 gl-context 中呈现它。为此,两个上下文都必须是 "shared",这是您通常在它们的构造函数中执行的操作。
但是,如果您的显卡无法在渲染时进行读取,则不会有很大的性能改进。