写入索引缓冲区时崩溃

Posted

技术标签:

【中文标题】写入索引缓冲区时崩溃【英文标题】:Crash when writing to an index buffer 【发布时间】:2015-04-18 07:01:36 【问题描述】:

我目前正在为 Windows、Mac 和 Linux 使用 C++11/SDL2/OpenGL 编写 an engine。

它在 Mac 和 Linux 上运行良好,但我的 Windows+Nvidia 桌面出现严重崩溃(我唯一拥有的其他 Windows 环境是虚拟机,它不支持我的 OpenGL 功能集)

我有两个朋友在不同的 Windows+AMD 设备上对其进行了测试,所以我的问题似乎与 Nvidia 的驱动程序以及我拥有它们的当前状态有关,这意味着 SSCCE 可能无济于事。 顶点缓冲区创建良好,并且以下索引缓冲区的创建使用在某个未知时间点正常工作。也许在驱动程序更新之前...

供参考,我的Buffer类如下:

static GLenum GetGLBufferType( BufferType bufferType ) 
    switch ( bufferType ) 
    case BufferType::Vertex: 
        return GL_ARRAY_BUFFER;
     break;

    case BufferType::Index: 
        return GL_ELEMENT_ARRAY_BUFFER;
     break;

    case BufferType::Uniform: 
        return GL_UNIFORM_BUFFER;
     break;

    default: 
        return GL_NONE;
     break;
    


GLuint Buffer::GetID( void ) const 
    return id;


Buffer::Buffer( BufferType bufferType, const void *data, size_t size )
: type( GetGLBufferType( bufferType ) ), offset( 0 ), size( size )

    glGenBuffers( 1, &id );
    glBindBuffer( type, id );
    glBufferData( type, size, data, GL_STREAM_DRAW );

    if ( bufferType == BufferType::Uniform ) 
        glGetIntegerv( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, reinterpret_cast<GLint *>( &alignment ) );
    
    else 
        alignment = 16;
    


Buffer::~Buffer() 
    glDeleteBuffers( 1, &id );


void *Buffer::Map( void ) 
    Bind();
    return glMapBufferRange( type, 0, size, GL_MAP_WRITE_BIT );


BufferMemory Buffer::MapDiscard( size_t allocSize ) 
    Bind();

    allocSize = (allocSize + alignment - 1) & ~(alignment - 1);
    if ( (offset + allocSize) > size ) 
        // We've run out of memory. Orphan the buffer and allocate some more memory
        glBufferData( type, size, nullptr, GL_STREAM_DRAW );
        offset = 0;
    

    BufferMemory result;
    result.devicePtr = glMapBufferRange(
        type,
        offset,
        allocSize,
        GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_RANGE_BIT
    );
    result.offset = offset;
    result.size = allocSize;
    offset += allocSize;
    return result;


void Buffer::Unmap( void ) 
    glUnmapBuffer( type );


void Buffer::BindRange( int index, size_t rangeOffset, size_t rangeSize ) const 
    if ( !rangeSize ) 
        rangeSize = size - rangeOffset;
    

    glBindBufferRange( type, index, id, rangeOffset, rangeSize );


void Buffer::Bind( void ) const 
    glBindBuffer( type, id );

创建索引缓冲区的代码如下所示:

static const uint16_t quadIndices[6] =  0, 2, 1, 1, 2, 3 ;
quadsIndexBuffer = new Buffer( BufferType::Index, quadIndices, sizeof(quadIndices) );

崩溃发生在glBufferData( type, size, data, GL_STREAM_DRAW );id 是 4type 是 34963 又名 GL_ELEMENT_ARRAY_BUFFERsize 是 12data 是 quadIndices

如果我尝试创建索引缓冲区而不填充它,然后像这样映射它并写入它:

quadsIndexBuffer = new Buffer( BufferType::Index, nullptr, sizeof(quadIndices) );
BufferMemory bufferMem = quadsIndexBuffer->MapDiscard( 6 * sizeof(uint16_t) );
uint16_t *indexBuffer = static_cast<uint16_t *>( bufferMem.devicePtr );
for ( size_t i = 0u; i < 6; i++ ) 
    *indexBuffer++ = quadIndices[i];

quadsIndexBuffer->Unmap();

然后崩溃发生在glMapBufferRange 内部Buffer::MapDiscard

该映射方法背后的基本原理是因为尝试映射繁忙的缓冲区会引入繁忙等待。

// Usage strategy is map-discard. In other words, keep appending to the buffer
// until we run out of memory. At this point, orphan the buffer by re-allocating
// a buffer of the same size and access bits.

我尝试四处寻找答案,但我发现的唯一解决方案与传递不正确的大小或 glBufferData 参数的错误顺序有关。没有帮助。

【问题讨论】:

你有什么问题? 为什么会崩溃,我该如何解决? 这是什么类型的崩溃?你收到什么信息? 在 0xC0000005 的访问冲突,调用堆栈显示垃圾,但这可能是由于我使用 /Z7 的编译器配置 - 我预计它在 nvoglv32.dll 中崩溃,因为这是执行控制最近传递给的地方 可能是一个愚蠢的建议,但在将缓冲区内容传递给 glBufferData 之前,请尝试将缓冲区内容复制到动态分配的(带有新的)内存块。 【参考方案1】:

似乎通过禁用GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB,崩溃不再表现出来,我的程序的行为是正确的。

我想我假设这是一个驱动程序错误是正确的。我会尝试将其转发给开发团队。

作为参考,这是针对 Nvidia GTX 680 驱动程序版本 350.12 上的 OpenGL 3.1 glewExperimental 已启用,并设置了以下 OpenGL 上下文标志:核心配置文件、前向兼容、调试

【讨论】:

以上是关于写入索引缓冲区时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

使用无锁队列(环形缓冲区)注意事项

MYSQL存储引擎InnoDB(二十三):排序索引构建

Java NIO使用及原理分析 来自网上资料整理

OpenGL 帧缓冲区缓慢且自发停止。广泛使用时甚至会导致系统崩溃

使用缓冲区写入文件时c ++内存泄漏

频繁更新 Texture2D 会导致进程崩溃 (UpdateSubresource)