glDrawArrays之间的glBufferSubData调用数据处理 [英] glBufferSubData between glDrawArrays calls mangling data

查看:89
本文介绍了glDrawArrays之间的glBufferSubData调用数据处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎glBufferSubData正在覆盖或以某种方式破坏我的glDrawArrays调用之间的数据.我正在使用Windows 7 64位操作系统,并具有适用于我的Nvidia GeForce GT520M CUDA 1GB的最新驱动程序.

我有2个模型,每个模型都有一个动画.这些模型有1个网格,并且该网格存储在同一VAO中.它们每个都有1个动画,用于渲染网格物体的骨骼变换存储在同一VBO中.

我的工作流程如下:

  • 计算模型的骨转化矩阵
  • 使用glBufferSubData将骨骼变换矩阵加载到opengl中,然后绑定缓冲区
  • 使用glDrawArrays
  • 渲染模型网格

对于一个模型,这是可行的(至少,在大多数情况下-有时我在顶点之间出现怪异的间隙).

但是,对于不止一个模型,看来在骨骼的渲染调用之间混合了骨骼变换矩阵数据.

单个模型动画Windows
两个模型制作了动画的Windows

我像这样加载我的骨骼变形数据:

void Animation::bind()
{
    glBindBuffer(GL_UNIFORM_BUFFER, bufferId_);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, currentTransforms_.size() * sizeof(glm::mat4), &currentTransforms_[0]);
    bindPoint_ = openGlDevice_->bindBuffer( bufferId_ );
}

然后像这样渲染我的网格:

void Mesh::render()
{
    glBindVertexArray(vaoId_);
    glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
    glBindVertexArray(0);
}

如果我在呼叫render()之后添加了对glFinish()的呼叫,则可以正常工作!这似乎向我表明,由于某种原因,一个动画的变换矩阵数据正在渗出"到下一个动画.

这怎么可能发生?我的印象是,如果在使用该缓冲区时调用了glBufferSubData(例如,对于glDrawArrays),则它将阻塞.不是吗?

值得一提的是,同样的代码在Linux中也能正常工作.

注意:与先前的帖子有关,该帖子我删除了.

网格加载代码:

void Mesh::load()
{
    LOG_DEBUG( "loading mesh '" + name_ +"' into video memory." );

    // create our vao
    glGenVertexArrays(1, &vaoId_);
    glBindVertexArray(vaoId_);

    // create our vbos
    glGenBuffers(5, &vboIds_[0]);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[0]);
    glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(glm::vec3), &vertices_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[1]);
    glBufferData(GL_ARRAY_BUFFER, textureCoordinates_.size() * sizeof(glm::vec2), &textureCoordinates_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[2]);
    glBufferData(GL_ARRAY_BUFFER, normals_.size() * sizeof(glm::vec3), &normals_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[3]);
    glBufferData(GL_ARRAY_BUFFER, colors_.size() * sizeof(glm::vec4), &colors_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, 0);

    if (bones_.size() == 0)
    {
        bones_.resize( vertices_.size() );
        for (auto& b : bones_)
        {
            b.weights = glm::vec4(0.25f);
        }
    }

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[4]);
    glBufferData(GL_ARRAY_BUFFER, bones_.size() * sizeof(VertexBoneData), &bones_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(4);
    glVertexAttribIPointer(4, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
    glEnableVertexAttribArray(5);
    glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)(sizeof(glm::ivec4)));

    glBindVertexArray(0);
}

动画UBO设置:

void Animation::setupAnimationUbo()
{
    bufferId_ = openGlDevice_->createBufferObject(GL_UNIFORM_BUFFER, Constants::MAX_NUMBER_OF_BONES_PER_MESH * sizeof(glm::mat4), &currentTransforms_[0]);
}

其中Constants::MAX_NUMBER_OF_BONES_PER_MESH设置为100.

OpenGlDevice中:

GLuint OpenGlDevice::createBufferObject(GLenum target, glmd::uint32 totalSize, const void* dataPointer)
{
    GLuint bufferId = 0;
    glGenBuffers(1, &bufferId);
    glBindBuffer(target, bufferId);

    glBufferData(target, totalSize, dataPointer, GL_DYNAMIC_DRAW);
    glBindBuffer(target, 0);

    bufferIds_.push_back(bufferId);

    return bufferId;
}

解决方案

尽管这种情况下您可以考虑尝试使用GL_STREAM_DRAW.

您的驱动程序由于某种原因似乎无法隐式同步,因此您可能想尝试一种首先消除同步需求的技术.我建议缓冲区孤立:在发送数据之前,将glBufferData (...)使用NULL调用数据指针.这将允许当前正在使用UBO的命令继续使用原始数据存储而不会强制进行同步,因为您将在发送新数据之前分配一个新的数据存储.前面提到的命令完成后,原始数据存储将被孤立,并且GL实现将释放它.

在较新的OpenGL实现中,您可以使用glInvalidateBuffer[Sub]Data (...)来提示驱动程序执行上面讨论的操作.同样,可以将glMapBufferRange (...)与适当的标志一起使用,以更明确地控制所有此行为.除非另有说明,否则取消映射将隐式刷新并同步对缓冲区对象的访问,如果您不想弄乱免同步的缓冲区更新逻辑,这可能会使驱动程序执行其工作.

此处中详细讨论了我提到的大部分内容.. >

It seems like glBufferSubData is overwriting or somehow mangling data between my glDrawArrays calls. I'm working in Windows 7 64bit, with that latest drivers for my Nvidia GeForce GT520M CUDA 1GB.

I have 2 models, each with an animation. The models have 1 mesh, and that mesh is stored in the same VAO. They also have 1 animation each, and the bone transformations to be used for rendering the mesh is stored in the same VBO.

My workflow looks like this:

  • calculate bone transformation matrices for a model
  • load bone transformation matrices into opengl using glBufferSubData, then bind the buffer
  • render the models mesh using glDrawArrays

For one model, this works (at least, mostly - sometimes I get weird gaps in between the vertices).

However, for more than one model, it looks like bone transformation matrix data is getting mixed up between the rendering calls to the meshes.

Single Model Animated Windows
Two Models Animated Windows

I load my bone transformation data like so:

void Animation::bind()
{
    glBindBuffer(GL_UNIFORM_BUFFER, bufferId_);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, currentTransforms_.size() * sizeof(glm::mat4), &currentTransforms_[0]);
    bindPoint_ = openGlDevice_->bindBuffer( bufferId_ );
}

And I render my mesh like so:

void Mesh::render()
{
    glBindVertexArray(vaoId_);
    glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
    glBindVertexArray(0);
}

If I add a call to glFinish() after my call to render(), it works just fine! This seems to indicate to me that, for some reason, the transformation matrix data for one animation is 'bleeding' over to the next animation.

How could this happen? I am under the impression that if I called glBufferSubData while that buffer was in use (i.e. for a glDrawArrays for example), then it would block. Is this not the case?

It might be worth mentioning that this same code works just fine in Linux.

Note: Related to a previous post, which I deleted.

Mesh Loading Code:

void Mesh::load()
{
    LOG_DEBUG( "loading mesh '" + name_ +"' into video memory." );

    // create our vao
    glGenVertexArrays(1, &vaoId_);
    glBindVertexArray(vaoId_);

    // create our vbos
    glGenBuffers(5, &vboIds_[0]);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[0]);
    glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(glm::vec3), &vertices_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[1]);
    glBufferData(GL_ARRAY_BUFFER, textureCoordinates_.size() * sizeof(glm::vec2), &textureCoordinates_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[2]);
    glBufferData(GL_ARRAY_BUFFER, normals_.size() * sizeof(glm::vec3), &normals_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[3]);
    glBufferData(GL_ARRAY_BUFFER, colors_.size() * sizeof(glm::vec4), &colors_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, 0);

    if (bones_.size() == 0)
    {
        bones_.resize( vertices_.size() );
        for (auto& b : bones_)
        {
            b.weights = glm::vec4(0.25f);
        }
    }

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[4]);
    glBufferData(GL_ARRAY_BUFFER, bones_.size() * sizeof(VertexBoneData), &bones_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(4);
    glVertexAttribIPointer(4, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
    glEnableVertexAttribArray(5);
    glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)(sizeof(glm::ivec4)));

    glBindVertexArray(0);
}

Animation UBO Setup:

void Animation::setupAnimationUbo()
{
    bufferId_ = openGlDevice_->createBufferObject(GL_UNIFORM_BUFFER, Constants::MAX_NUMBER_OF_BONES_PER_MESH * sizeof(glm::mat4), &currentTransforms_[0]);
}

where Constants::MAX_NUMBER_OF_BONES_PER_MESH is set to 100.

In OpenGlDevice:

GLuint OpenGlDevice::createBufferObject(GLenum target, glmd::uint32 totalSize, const void* dataPointer)
{
    GLuint bufferId = 0;
    glGenBuffers(1, &bufferId);
    glBindBuffer(target, bufferId);

    glBufferData(target, totalSize, dataPointer, GL_DYNAMIC_DRAW);
    glBindBuffer(target, 0);

    bufferIds_.push_back(bufferId);

    return bufferId;
}

解决方案

Those usage flags are mostly correct for this scenario, though you might consider trying GL_STREAM_DRAW.

Your driver appears to be failing to implicitly synchronize for some reason, so you might want to try a technique that eliminates the need for synchronization in the first place. I would suggest Buffer Orphaning: call glBufferData (...) with NULL for the data pointer prior to sending data. This will allow commands that are currently using the UBO to continue using the original data store without forcing synchronization, since you will allocate a new data store before sending new data. When the earlier mentioned commands finish the original data store will be orphaned and the GL implementation will free it.

In newer OpenGL implementations you can use glInvalidateBuffer[Sub]Data (...) to hint the driver into doing what was discussed above. Likewise, you can use glMapBufferRange (...) with appropriate flags to control all of this behavior more explicitly. Unmapping will implicitly flush and synchronize access to a buffer object unless told otherwise, this might get your driver to do its job if you do not want to mess around with synchronization-free buffer update logic.

Most of what I mentioned is discussed in more detail here.

这篇关于glDrawArrays之间的glBufferSubData调用数据处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆