[英]When instanced rendering, using a transform vbo with glVertexAttribDivisor messes up glDrawElements
[英]Why glDrawElements is interfering with glGetBufferSubData when using the same buffer for Compute Shading and rendering
我有一個渲染點流的程序。 用於獲取給定幀的點的方法具有很強的時間一致性,因此,在渲染循環中,首先,我使用Compute Shader壓縮流,去除不需要的點。 其次,我將新點添加到集合中。 最后,我使用glDrawElements
渲染它。
這就是執行該過程的代碼(它使用Qt操作OpenGL):
template< typename Vec3 >
unsigned int CompactionRenderingState< Vec3 >::render()
{
// Compact stream.
m_nElements = compact();
// Sends new points to GPU.
QOpenGLBuffer* buffer = m_outputBuffers[ POS ];
buffer->bind();
buffer->write( m_nElements * BYTES_PER_VERTEX, ( void * ) &RenderingState::m_positions[ 0 ],
RenderingState::m_positions.size() * BYTES_PER_VERTEX );
buffer = m_outputBuffers[ ATTRIB0 ];
buffer->bind();
buffer->write( m_nElements * BYTES_PER_VERTEX, ( void * ) &RenderingState::m_colors[ 0 ],
RenderingState::m_colors.size() * BYTES_PER_VERTEX );
m_nElements += RenderingState::m_positions.size();
// Draws the resulting points.
m_arrayObj->bind();
unsigned int bufferOffset = 0;
switch( RenderingState::m_attribs )
{
case Attributes::NORMALS:
{
RenderingState::m_painter->setStandardEffect( QGL::LitMaterial );
m_outputBuffers[ POS ]->bind();
m_openGL->glVertexAttribPointer( QGL::Position, 3, GL_FLOAT, GL_FALSE, 0, &bufferOffset );
m_openGL->glEnableVertexAttribArray( QGL::Position );
m_outputBuffers[ ATTRIB0 ]->bind();
m_openGL->glVertexAttribPointer( QGL::Normal, 3, GL_FLOAT, GL_FALSE, 0, &bufferOffset );
m_openGL->glEnableVertexAttribArray( QGL::Normal );
break;
}
case Attributes::COLORS:
{
m_renderingProgram->bind();
m_outputBuffers[ POS ]->bind();
m_openGL->glVertexAttribPointer( QGL::Position, 3, GL_FLOAT, GL_FALSE, 0, &bufferOffset );
m_openGL->glEnableVertexAttribArray( QGL::Position );
m_outputBuffers[ ATTRIB0 ]->bind();
m_openGL->glVertexAttribPointer( QGL::Color, 3, GL_FLOAT, GL_FALSE, 0, &bufferOffset );
m_openGL->glEnableVertexAttribArray( QGL::Color );
break;
}
case Attributes::COLORS_AND_NORMALS:
{
throw logic_error( "Colors and normals not supported yet." );
break;
}
}
m_openGL->glMemoryBarrier( GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT );
m_openGL->glDrawArrays( GL_POINTS, 0, m_nElements );
m_openGL->glDisableVertexAttribArray( QGL::Position );
m_openGL->glDisableVertexAttribArray( QGL::Normal );
m_openGL->glDisableVertexAttribArray( QGL::Color );
m_openGL->glBindBuffer( GL_ARRAY_BUFFER, 0 );
m_renderingProgram->release();
m_arrayObj->release();
// Swaps buffers for the next frame.
for( int i = 0; i < N_BUFFER_TYPES; ++i )
{
std::swap( m_inputBuffers[ i ], m_outputBuffers[ i ] );
}
}
template< typename Vec3 >
unsigned int CompactionRenderingState< Vec3 >::compact()
{
// Makes the compaction of the unused points.
unsigned int nElements = m_compactionFlags.size();
unsigned int nBlocks = ( unsigned int ) ceil( ( float ) nElements / BLOCK_SIZE );
nElements = m_scan.doScan( m_compactionFlags );
m_openGL->glBindBufferBase( GL_SHADER_STORAGE_BUFFER, Scan::N_BUFFER_TYPES + POS, m_inputBuffers[ POS ]->bufferId() );
m_openGL->glBindBufferBase( GL_SHADER_STORAGE_BUFFER, Scan::N_BUFFER_TYPES + ATTRIB0,
m_inputBuffers[ ATTRIB0 ]->bufferId() );
m_openGL->glBindBufferBase( GL_SHADER_STORAGE_BUFFER, Scan::N_BUFFER_TYPES + N_BUFFER_TYPES + POS,
m_outputBuffers[ POS ]->bufferId() );
m_openGL->glBindBufferBase( GL_SHADER_STORAGE_BUFFER, Scan::N_BUFFER_TYPES + N_BUFFER_TYPES + ATTRIB0,
m_outputBuffers[ ATTRIB0 ]->bufferId() );
m_compactionProgram->bind();
m_compactionProgram->enableAttributeArray( "flags" );
m_compactionProgram->enableAttributeArray( "prefixes" );
m_compactionProgram->enableAttributeArray( "inputVertices" );
m_compactionProgram->enableAttributeArray( "inputAttrib0" );
m_compactionProgram->enableAttributeArray( "outputVertices" );
m_compactionProgram->enableAttributeArray( "outputAttrib0" );
m_openGL->glDispatchCompute( nBlocks, 1, 1 );
m_openGL->glMemoryBarrier( GL_SHADER_STORAGE_BARRIER_BIT );
m_compactionProgram->disableAttributeArray( "flags" );
m_compactionProgram->disableAttributeArray( "prefixes" );
m_compactionProgram->disableAttributeArray( "inputVertices" );
m_compactionProgram->disableAttributeArray( "inputAttrib0" );
m_compactionProgram->disableAttributeArray( "outputVertices" );
m_compactionProgram->disableAttributeArray( "outputAttrib0" );
m_openGL->glBindBuffer( GL_SHADER_STORAGE_BUFFER, 0 );
return nElements;
}
======編輯======
我寫了一個自動化測試來檢查整個過程。 此測試壓縮點位置數組和另一個屬性數組,以致刪除奇數索引中的數據。 當我在禁用地址空間隨機化的gdb下運行代碼時,它可以完美地通過。 但是,在沒有gdb或gdb中啟用了addres空間隨機化的情況下運行時,除非我在render()
方法中注釋glDrawArrays
,否則返回的數組都為零。
======編輯結束======
TEST_F( CompactionTest, Compaction )
{
QGuiApplication app( g_argc, g_argv );
QSurfaceFormat format;
format.setVersion( 4, 3 );
format.setRenderableType( QSurfaceFormat::OpenGL );
format.setSwapBehavior( QSurfaceFormat::DoubleBuffer );
format.setSamples( 16 );
unsigned int nElements = 3000;
vector< unsigned int > flags( nElements );
vector< vec3 > pos( nElements );
vector< vec3 > attrib0( nElements );
for( int i = 0; i < nElements; ++i )
{
flags[ i ] = i % 2;
pos[ i ] = vec3( i, i, i );
attrib0[ i ] = vec3( i + nElements, i + nElements, i + nElements );
}
CompactionQGLView window( flags, pos, attrib0, format );
window.resize(640, 480);
window.show();
app.exec();
pos = window.m_compactedPos;
attrib0 = window.m_compactedAttrib0;
ASSERT_EQ( pos.size(), nElements * 0.5 );
ASSERT_EQ( attrib0.size(), nElements * 0.5 );
float expected = 1.;
for( int i = 0; i < pos.size(); ++i, expected += 2 )
{
vec3 expectedVec( expected, expected, expected );
cout << "Pos: " << pos[ i ] << ". Expected: " << expectedVec << endl;
ASSERT_EQ( pos[ i ], expectedVec );
expectedVec = vec3( expected + nElements, expected + nElements, expected + nElements );
cout << "Attrib0: " << attrib0[ i ] << ". Expected: " << expectedVec << endl << endl;
ASSERT_EQ( attrib0[ i ], expectedVec );
}
}
下一個函數讀取壓縮結果,測試中的window
將其用於設置m_compactedPos
和m_compactedAttrib0
。
template< typename Vec3 >
vector< vector< Vec3 > > CompactionRenderingState< Vec3 >::getResultCPU()
{
m_openGL->glMemoryBarrier( GL_SHADER_STORAGE_BARRIER_BIT );
unsigned int resultSize = sizeof( Vec3 ) * m_nElements;
Vec3* result = ( Vec3* ) malloc( resultSize );
vector< vector< Vec3 > > results;
for( int i = 0; i < N_BUFFER_TYPES; ++i )
{
if( m_inputBuffers[ i ] != NULL )
{
m_openGL->glBindBuffer( GL_SHADER_STORAGE_BUFFER, m_inputBuffers[ i ]->bufferId() );
m_openGL->glGetBufferSubData( GL_SHADER_STORAGE_BUFFER, 0, resultSize, ( void * ) result );
vector< Vec3 > tempVec( m_nElements );
std::copy( result, result + m_nElements, tempVec.begin() );
results.push_back( tempVec );
}
}
free( result );
return results;
}
======編輯======
那么,僅當啟用“地址空間隨機化”時,才可能出現此錯誤的原因是什么? 我被困住了,已經幾天試圖解決這個問題了。 有任何想法嗎?
======編輯結束======
最后,問題是調用glVertexAttribPointer時發生了一個錯誤的隱式類型轉換。
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
接收作為最后一個參數一個const GLvoid * pointer
。 在我的代碼中,我將變量unsigned int bufferOffset
的地址作為最后一個參數傳遞。 那應該導致OpenGL內部混亂。
要修復代碼,我們只需要在render()
函數的switch
子句中更改glVertexAttribPointer
調用即可:
switch( RenderingState::m_attribs )
{
case Attributes::NORMALS:
{
RenderingState::m_painter->setStandardEffect( QGL::LitMaterial );
m_outputBuffers[ POS ]->bind();
m_openGL->glVertexAttribPointer( QGL::Position, 3, GL_FLOAT, GL_FALSE, 0, ( void * ) 0 );
m_openGL->glEnableVertexAttribArray( QGL::Position );
m_outputBuffers[ ATTRIB0 ]->bind();
m_openGL->glVertexAttribPointer( QGL::Normal, 3, GL_FLOAT, GL_FALSE, 0, ( void * ) 0 );
m_openGL->glEnableVertexAttribArray( QGL::Normal );
break;
}
case Attributes::COLORS:
{
m_renderingProgram->bind();
m_outputBuffers[ POS ]->bind();
m_openGL->glVertexAttribPointer( QGL::Position, 3, GL_FLOAT, GL_FALSE, 0, ( void * ) 0 );
m_openGL->glEnableVertexAttribArray( QGL::Position );
m_outputBuffers[ ATTRIB0 ]->bind();
m_openGL->glVertexAttribPointer( QGL::Color, 3, GL_FLOAT, GL_FALSE, 0, ( void * ) 0 );
m_openGL->glEnableVertexAttribArray( QGL::Color );
break;
}
case Attributes::COLORS_AND_NORMALS:
{
throw logic_error( "Colors and normals not supported yet." );
break;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.