Fixes for video cards unable to use gl{Un,}MapBuffer correctly

Fixes: lp:1685335
* https://bugs.launchpad.net/kicad/+bug/1685335

Fixes: lp:1683041
* https://bugs.launchpad.net/kicad/+bug/1683041
This commit is contained in:
Maciej Suminski 2017-08-08 14:25:03 +02:00
parent 46b5575c51
commit 262fcc91af
14 changed files with 683 additions and 344 deletions

View File

@ -49,6 +49,8 @@ set( GAL_SRCS
gal/opengl/vertex_item.cpp
gal/opengl/vertex_container.cpp
gal/opengl/cached_container.cpp
gal/opengl/cached_container_gpu.cpp
gal/opengl/cached_container_ram.cpp
gal/opengl/noncached_container.cpp
gal/opengl/vertex_manager.cpp
gal/opengl/gpu_manager.cpp

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -32,10 +32,8 @@
#include <gal/opengl/cached_container.h>
#include <gal/opengl/vertex_manager.h>
#include <gal/opengl/vertex_item.h>
#include <gal/opengl/shader.h>
#include <gal/opengl/utils.h>
#include <confirm.h>
#include <list>
#include <cassert>
@ -47,27 +45,13 @@
using namespace KIGFX;
CACHED_CONTAINER::CACHED_CONTAINER( unsigned int aSize ) :
VERTEX_CONTAINER( aSize ), m_item( NULL ),
m_chunkSize( 0 ), m_chunkOffset( 0 ), m_isMapped( false ),
m_isInitialized( false ), m_glBufferHandle( -1 )
VERTEX_CONTAINER( aSize ), m_item( NULL ), m_chunkSize( 0 ), m_chunkOffset( 0 )
{
// In the beginning there is only free space
m_freeChunks.insert( std::make_pair( aSize, 0 ) );
}
CACHED_CONTAINER::~CACHED_CONTAINER()
{
if( m_isMapped )
Unmap();
if( m_isInitialized )
{
glDeleteBuffers( 1, &m_glBufferHandle );
}
}
void CACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem )
{
assert( aItem != NULL );
@ -75,7 +59,6 @@ void CACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem )
unsigned int itemSize = aItem->GetSize();
m_item = aItem;
m_chunkSize = itemSize;
m_useCopyBuffer = GLEW_ARB_copy_buffer;
// Get the previously set offset if the item was stored previously
m_chunkOffset = itemSize > 0 ? aItem->GetOffset() : -1;
@ -120,7 +103,7 @@ void CACHED_CONTAINER::FinishItem()
VERTEX* CACHED_CONTAINER::Allocate( unsigned int aSize )
{
assert( m_item != NULL );
assert( m_isMapped );
assert( IsMapped() );
if( m_failed )
return NULL;
@ -211,9 +194,7 @@ void CACHED_CONTAINER::Clear()
// Set the size of all the stored VERTEX_ITEMs to 0, so it is clear that they are not held
// in the container anymore
for( ITEMS::iterator it = m_items.begin(); it != m_items.end(); ++it )
{
( *it )->setSize( 0 );
}
m_items.clear();
@ -223,51 +204,10 @@ void CACHED_CONTAINER::Clear()
}
void CACHED_CONTAINER::Map()
{
assert( !IsMapped() );
if( !m_isInitialized )
init();
glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
checkGlError( "mapping vertices buffer" );
m_isMapped = true;
}
void CACHED_CONTAINER::Unmap()
{
assert( IsMapped() );
glUnmapBuffer( GL_ARRAY_BUFFER );
checkGlError( "unmapping vertices buffer" );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
m_vertices = NULL;
checkGlError( "unbinding vertices buffer" );
m_isMapped = false;
}
void CACHED_CONTAINER::init()
{
glGenBuffers( 1, &m_glBufferHandle );
glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
glBufferData( GL_ARRAY_BUFFER, m_currentSize * VertexSize, NULL, GL_DYNAMIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
checkGlError( "allocating video memory for cached container" );
m_isInitialized = true;
}
bool CACHED_CONTAINER::reallocate( unsigned int aSize )
{
assert( aSize > 0 );
assert( m_isMapped );
assert( IsMapped() );
unsigned int itemSize = m_item->GetSize();
@ -336,6 +276,38 @@ bool CACHED_CONTAINER::reallocate( unsigned int aSize )
}
void CACHED_CONTAINER::defragment( VERTEX* aTarget )
{
// Defragmentation
ITEMS::iterator it, it_end;
int newOffset = 0;
for( VERTEX_ITEM* item : m_items )
{
int itemOffset = item->GetOffset();
int itemSize = item->GetSize();
// Move an item to the new container
memcpy( &aTarget[newOffset], &m_vertices[itemOffset], itemSize * VertexSize );
// Update new offset
item->setOffset( newOffset );
// Move to the next free space
newOffset += itemSize;
}
// Move the current item and place it at the end
if( m_item->GetSize() > 0 )
{
memcpy( &aTarget[newOffset], &m_vertices[m_item->GetOffset()],
m_item->GetSize() * VertexSize );
m_item->setOffset( newOffset );
m_chunkOffset = newOffset;
}
}
void CACHED_CONTAINER::mergeFreeChunks()
{
if( m_freeChunks.size() <= 1 ) // There are no chunks that can be merged
@ -395,200 +367,6 @@ void CACHED_CONTAINER::mergeFreeChunks()
}
bool CACHED_CONTAINER::defragmentResize( unsigned int aNewSize )
{
if( !m_useCopyBuffer )
return defragmentResizeMemcpy( aNewSize );
assert( IsMapped() );
wxLogTrace( "GAL_CACHED_CONTAINER",
wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize, aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef __WXDEBUG__
PROF_COUNTER totalTime;
#endif /* __WXDEBUG__ */
GLuint newBuffer;
// glCopyBufferSubData requires a buffer to be unmapped
glUnmapBuffer( GL_ARRAY_BUFFER );
// Create the destination buffer
glGenBuffers( 1, &newBuffer );
// It would be best to use GL_COPY_WRITE_BUFFER here,
// but it is not available everywhere
#ifdef __WXDEBUG__
GLint eaBuffer = -1;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
assert( eaBuffer == 0 );
#endif /* __WXDEBUG__ */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VertexSize, NULL, GL_DYNAMIC_DRAW );
checkGlError( "creating buffer during defragmentation" );
ITEMS::iterator it, it_end;
int newOffset = 0;
// Defragmentation
for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
{
VERTEX_ITEM* item = *it;
int itemOffset = item->GetOffset();
int itemSize = item->GetSize();
// Move an item to the new container
glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
itemOffset * VertexSize, newOffset * VertexSize, itemSize * VertexSize );
// Update new offset
item->setOffset( newOffset );
// Move to the next free space
newOffset += itemSize;
}
// Move the current item and place it at the end
if( m_item->GetSize() > 0 )
{
glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
m_item->GetOffset() * VertexSize, newOffset * VertexSize,
m_item->GetSize() * VertexSize );
m_item->setOffset( newOffset );
m_chunkOffset = newOffset;
}
// Cleanup
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
// Previously we have unmapped the array buffer, now when it is also
// unbound, it may be officially marked as unmapped
m_isMapped = false;
glDeleteBuffers( 1, &m_glBufferHandle );
// Switch to the new vertex buffer
m_glBufferHandle = newBuffer;
Map();
checkGlError( "switching buffers during defragmentation" );
#ifdef __WXDEBUG__
totalTime.Stop();
wxLogTrace( "GAL_CACHED_CONTAINER",
"Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* __WXDEBUG__ */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}
bool CACHED_CONTAINER::defragmentResizeMemcpy( unsigned int aNewSize )
{
assert( IsMapped() );
wxLogTrace( "GAL_CACHED_CONTAINER",
wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ),
m_currentSize, aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef __WXDEBUG__
PROF_COUNTER totalTime;
#endif /* __WXDEBUG__ */
GLuint newBuffer;
VERTEX* newBufferMem;
// Create the destination buffer
glGenBuffers( 1, &newBuffer );
// It would be best to use GL_COPY_WRITE_BUFFER here,
// but it is not available everywhere
#ifdef __WXDEBUG__
GLint eaBuffer = -1;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
assert( eaBuffer == 0 );
#endif /* __WXDEBUG__ */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VertexSize, NULL, GL_DYNAMIC_DRAW );
newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
checkGlError( "creating buffer during defragmentation" );
// Defragmentation
ITEMS::iterator it, it_end;
int newOffset = 0;
for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
{
VERTEX_ITEM* item = *it;
int itemOffset = item->GetOffset();
int itemSize = item->GetSize();
// Move an item to the new container
memcpy( &newBufferMem[newOffset], &m_vertices[itemOffset], itemSize * VertexSize );
// Update new offset
item->setOffset( newOffset );
// Move to the next free space
newOffset += itemSize;
}
// Move the current item and place it at the end
if( m_item->GetSize() > 0 )
{
memcpy( &newBufferMem[newOffset], &m_vertices[m_item->GetOffset()],
m_item->GetSize() * VertexSize );
m_item->setOffset( newOffset );
m_chunkOffset = newOffset;
}
// Cleanup
glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
Unmap();
glDeleteBuffers( 1, &m_glBufferHandle );
// Switch to the new vertex buffer
m_glBufferHandle = newBuffer;
Map();
checkGlError( "switching buffers during defragmentation" );
#ifdef __WXDEBUG__
totalTime.Stop();
wxLogTrace( "GAL_CACHED_CONTAINER",
"Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* __WXDEBUG__ */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}
void CACHED_CONTAINER::addFreeChunk( unsigned int aOffset, unsigned int aSize )
{
assert( aOffset + aSize <= m_currentSize );
@ -669,4 +447,3 @@ void CACHED_CONTAINER::test()
// Overlapping check TODO
#endif /* __WXDEBUG__ */
}

View File

@ -0,0 +1,265 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <gal/opengl/cached_container_gpu.h>
#include <gal/opengl/vertex_manager.h>
#include <gal/opengl/vertex_item.h>
#include <gal/opengl/shader.h>
#include <gal/opengl/utils.h>
#include <list>
#include <cassert>
#ifdef __WXDEBUG__
#include <wx/log.h>
#include <profile.h>
#endif /* __WXDEBUG__ */
using namespace KIGFX;
CACHED_CONTAINER_GPU::CACHED_CONTAINER_GPU( unsigned int aSize ) :
CACHED_CONTAINER( aSize ), m_isMapped( false ),
m_isInitialized( false ), m_glBufferHandle( -1 )
{
}
CACHED_CONTAINER_GPU::~CACHED_CONTAINER_GPU()
{
if( m_isMapped )
Unmap();
if( m_isInitialized )
glDeleteBuffers( 1, &m_glBufferHandle );
}
void CACHED_CONTAINER_GPU::Map()
{
assert( !IsMapped() );
if( !m_isInitialized )
init();
glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
checkGlError( "mapping vertices buffer" );
m_isMapped = true;
}
void CACHED_CONTAINER_GPU::Unmap()
{
assert( IsMapped() );
glUnmapBuffer( GL_ARRAY_BUFFER );
checkGlError( "unmapping vertices buffer" );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
m_vertices = NULL;
checkGlError( "unbinding vertices buffer" );
m_isMapped = false;
}
void CACHED_CONTAINER_GPU::init()
{
m_useCopyBuffer = GLEW_ARB_copy_buffer;
glGenBuffers( 1, &m_glBufferHandle );
glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
glBufferData( GL_ARRAY_BUFFER, m_currentSize * VertexSize, NULL, GL_DYNAMIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
checkGlError( "allocating video memory for cached container" );
m_isInitialized = true;
}
bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
{
if( !m_useCopyBuffer )
return defragmentResizeMemcpy( aNewSize );
assert( IsMapped() );
wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize, aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef __WXDEBUG__
PROF_COUNTER totalTime;
#endif /* __WXDEBUG__ */
GLuint newBuffer;
// glCopyBufferSubData requires a buffer to be unmapped
glUnmapBuffer( GL_ARRAY_BUFFER );
// Create the destination buffer
glGenBuffers( 1, &newBuffer );
// It would be best to use GL_COPY_WRITE_BUFFER here,
// but it is not available everywhere
#ifdef __WXDEBUG__
GLint eaBuffer = -1;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
assert( eaBuffer == 0 );
#endif /* __WXDEBUG__ */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VertexSize, NULL, GL_DYNAMIC_DRAW );
checkGlError( "creating buffer during defragmentation" );
ITEMS::iterator it, it_end;
int newOffset = 0;
// Defragmentation
for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
{
VERTEX_ITEM* item = *it;
int itemOffset = item->GetOffset();
int itemSize = item->GetSize();
// Move an item to the new container
glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
itemOffset * VertexSize, newOffset * VertexSize, itemSize * VertexSize );
// Update new offset
item->setOffset( newOffset );
// Move to the next free space
newOffset += itemSize;
}
// Move the current item and place it at the end
if( m_item->GetSize() > 0 )
{
glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
m_item->GetOffset() * VertexSize, newOffset * VertexSize,
m_item->GetSize() * VertexSize );
m_item->setOffset( newOffset );
m_chunkOffset = newOffset;
}
// Cleanup
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
// Previously we have unmapped the array buffer, now when it is also
// unbound, it may be officially marked as unmapped
m_isMapped = false;
glDeleteBuffers( 1, &m_glBufferHandle );
// Switch to the new vertex buffer
m_glBufferHandle = newBuffer;
Map();
checkGlError( "switching buffers during defragmentation" );
#ifdef __WXDEBUG__
totalTime.Stop();
wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
"Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* __WXDEBUG__ */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}
bool CACHED_CONTAINER_GPU::defragmentResizeMemcpy( unsigned int aNewSize )
{
assert( IsMapped() );
wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ),
m_currentSize, aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef __WXDEBUG__
PROF_COUNTER totalTime;
#endif /* __WXDEBUG__ */
GLuint newBuffer;
VERTEX* newBufferMem;
// Create the destination buffer
glGenBuffers( 1, &newBuffer );
// It would be best to use GL_COPY_WRITE_BUFFER here,
// but it is not available everywhere
#ifdef __WXDEBUG__
GLint eaBuffer = -1;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
assert( eaBuffer == 0 );
#endif /* __WXDEBUG__ */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VertexSize, NULL, GL_DYNAMIC_DRAW );
newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
checkGlError( "creating buffer during defragmentation" );
defragment( newBufferMem );
// Cleanup
glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
Unmap();
glDeleteBuffers( 1, &m_glBufferHandle );
// Switch to the new vertex buffer
m_glBufferHandle = newBuffer;
Map();
checkGlError( "switching buffers during defragmentation" );
#ifdef __WXDEBUG__
totalTime.Stop();
wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
"Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* __WXDEBUG__ */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}

View File

@ -0,0 +1,121 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <gal/opengl/cached_container_ram.h>
#include <gal/opengl/vertex_manager.h>
#include <gal/opengl/vertex_item.h>
#include <gal/opengl/shader.h>
#include <gal/opengl/utils.h>
#include <confirm.h>
#include <list>
#include <cassert>
#ifdef __WXDEBUG__
#include <wx/log.h>
#include <profile.h>
#endif /* __WXDEBUG__ */
using namespace KIGFX;
CACHED_CONTAINER_RAM::CACHED_CONTAINER_RAM( unsigned int aSize ) :
CACHED_CONTAINER( aSize ), m_isInitialized( false ), m_verticesBuffer( 0 )
{
m_vertices = static_cast<VERTEX*>( malloc( aSize * VertexSize ) );
}
CACHED_CONTAINER_RAM::~CACHED_CONTAINER_RAM()
{
if( m_isInitialized )
glDeleteBuffers( 1, &m_verticesBuffer );
free( m_vertices );
}
void CACHED_CONTAINER_RAM::Unmap()
{
if( !m_dirty )
return;
if( !m_isInitialized )
{
glGenBuffers( 1, &m_verticesBuffer );
checkGlError( "generating vertices buffer" );
m_isInitialized = true;
}
// Upload vertices coordinates and shader types to GPU memory
glBindBuffer( GL_ARRAY_BUFFER, m_verticesBuffer );
checkGlError( "binding vertices buffer" );
glBufferData( GL_ARRAY_BUFFER, usedSpace() * VertexSize, m_vertices, GL_STREAM_DRAW );
checkGlError( "transferring vertices" );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
checkGlError( "unbinding vertices buffer" );
}
bool CACHED_CONTAINER_RAM::defragmentResize( unsigned int aNewSize )
{
wxLogTrace( "GAL_CACHED_CONTAINER",
wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ),
m_currentSize, aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef __WXDEBUG__
PROF_COUNTER totalTime;
#endif /* __WXDEBUG__ */
VERTEX* newBufferMem = static_cast<VERTEX*>( malloc( aNewSize * VertexSize ) );
if( !newBufferMem )
return false;
defragment( newBufferMem );
// Switch to the new vertex buffer
free( m_vertices );
m_vertices = newBufferMem;
#ifdef __WXDEBUG__
totalTime.Stop();
wxLogTrace( "GAL_CACHED_CONTAINER",
"Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* __WXDEBUG__ */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -22,13 +22,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file gpu_manager.cpp
* @brief Class to handle uploading vertices and indices to GPU in drawing purposes.
*/
#include <gal/opengl/gpu_manager.h>
#include <gal/opengl/cached_container.h>
#include <gal/opengl/cached_container_gpu.h>
#include <gal/opengl/cached_container_ram.h>
#include <gal/opengl/noncached_container.h>
#include <gal/opengl/shader.h>
#include <gal/opengl/utils.h>
@ -45,13 +41,10 @@ using namespace KIGFX;
GPU_MANAGER* GPU_MANAGER::MakeManager( VERTEX_CONTAINER* aContainer )
{
if( typeid( *aContainer ) == typeid( CACHED_CONTAINER ) )
if( aContainer->IsCached() )
return new GPU_CACHED_MANAGER( aContainer );
else if( typeid( *aContainer ) == typeid( NONCACHED_CONTAINER ) )
else
return new GPU_NONCACHED_MANAGER( aContainer );
wxASSERT_MSG( false, wxT( "Not handled container type" ) );
return NULL;
}

View File

@ -87,10 +87,6 @@ OPENGL_GAL::OPENGL_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
compositor = new OPENGL_COMPOSITOR;
compositor->SetAntialiasingMode( options.gl_antialiasing_mode );
cachedManager = new VERTEX_MANAGER( true );
nonCachedManager = new VERTEX_MANAGER( false );
overlayManager = new VERTEX_MANAGER( false );
// Initialize the flags
isFramebufferInitialized = false;
isBitmapFontInitialized = false;
@ -154,9 +150,13 @@ OPENGL_GAL::~OPENGL_GAL()
ClearCache();
delete compositor;
delete cachedManager;
delete nonCachedManager;
delete overlayManager;
if( isInitialized )
{
delete cachedManager;
delete nonCachedManager;
delete overlayManager;
}
GL_CONTEXT_MANAGER::Get().UnlockCtx( glPrivContext );
@ -1251,7 +1251,9 @@ void OPENGL_GAL::DeleteGroup( int aGroupNumber )
void OPENGL_GAL::ClearCache()
{
groups.clear();
cachedManager->Clear();
if( isInitialized )
cachedManager->Clear();
}
@ -1762,6 +1764,10 @@ void OPENGL_GAL::init()
throw;
}
cachedManager = new VERTEX_MANAGER( true );
nonCachedManager = new VERTEX_MANAGER( false );
overlayManager = new VERTEX_MANAGER( false );
// Make VBOs use shaders
cachedManager->SetShader( *shader );
nonCachedManager->SetShader( *shader );

View File

@ -28,18 +28,30 @@
*/
#include <gal/opengl/vertex_container.h>
#include <gal/opengl/cached_container.h>
#include <gal/opengl/cached_container_ram.h>
#include <gal/opengl/cached_container_gpu.h>
#include <gal/opengl/noncached_container.h>
#include <gal/opengl/shader.h>
#include <cstring>
using namespace KIGFX;
VERTEX_CONTAINER* VERTEX_CONTAINER::MakeContainer( bool aCached )
{
if( aCached )
return new CACHED_CONTAINER;
else
return new NONCACHED_CONTAINER;
{
const unsigned char* vendor = glGetString( GL_VENDOR );
// AMD/ATI cards do not cope well with GPU memory mapping,
// so the vertex data has to be kept in RAM
if( strstr( (const char*) vendor, "AMD" ) )
return new CACHED_CONTAINER_RAM;
else
return new CACHED_CONTAINER_GPU;
}
return new NONCACHED_CONTAINER;
}

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -22,13 +22,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file cached_container.h
* @brief Class to store instances of VERTEX with caching. It allows storing VERTEX objects and
* associates them with VERTEX_ITEMs. This leads to a possibility of caching vertices data in the
* GPU memory and a fast reuse of that data.
*/
#ifndef CACHED_CONTAINER_H_
#define CACHED_CONTAINER_H_
@ -41,11 +34,22 @@ namespace KIGFX
class VERTEX_ITEM;
class SHADER;
/**
* @brief Class to store VERTEX instances with caching. It associates VERTEX
* objects and with VERTEX_ITEMs. Caching vertices data in the memory and a
* enables fast reuse of that data.
*/
class CACHED_CONTAINER : public VERTEX_CONTAINER
{
public:
CACHED_CONTAINER( unsigned int aSize = defaultInitSize );
~CACHED_CONTAINER();
CACHED_CONTAINER( unsigned int aSize = DEFAULT_SIZE );
virtual ~CACHED_CONTAINER() {}
bool IsCached() const override
{
return true;
}
///> @copydoc VERTEX_CONTAINER::SetItem()
virtual void SetItem( VERTEX_ITEM* aItem ) override;
@ -66,25 +70,19 @@ public:
* Function GetBufferHandle()
* returns handle to the vertex buffer. It might be negative if the buffer is not initialized.
*/
inline unsigned int GetBufferHandle() const
{
return m_glBufferHandle;
}
virtual unsigned int GetBufferHandle() const = 0;
/**
* Function IsMapped()
* returns true if vertex buffer is currently mapped.
*/
inline bool IsMapped() const
{
return m_isMapped;
}
virtual bool IsMapped() const = 0;
///> @copydoc VERTEX_CONTAINER::Map()
void Map() override;
virtual void Map() override = 0;
///> @copydoc VERTEX_CONTAINER::Unmap()
void Unmap() override;
virtual void Unmap() override = 0;
protected:
///> Maps size of free memory chunks to their offsets
@ -107,25 +105,6 @@ protected:
unsigned int m_chunkSize;
unsigned int m_chunkOffset;
///> Flag saying if vertex buffer is currently mapped
bool m_isMapped;
///> Flag saying if the vertex buffer is initialized
bool m_isInitialized;
///> Vertex buffer handle
unsigned int m_glBufferHandle;
///> Flag saying whether it is safe to use glCopyBufferSubData
bool m_useCopyBuffer;
/**
* Function init()
* performs the GL vertex buffer initialization. It can be invoked only when an OpenGL context
* is bound.
*/
void init();
/**
* Function reallocate()
* resizes the chunk that stores the current item to the given size. The current item has
@ -145,8 +124,14 @@ protected:
* @param aNewSize is the new size of container, expressed in number of vertices
* @return false in case of failure (e.g. memory shortage)
*/
bool defragmentResize( unsigned int aNewSize );
bool defragmentResizeMemcpy( unsigned int aNewSize );
virtual bool defragmentResize( unsigned int aNewSize ) = 0;
/**
* Transfers all stored data to a new buffer, removing empty spaces between the data chunks
* in the container.
* @param aTarget is the destination for the defragmented data.
*/
void defragment( VERTEX* aTarget );
/**
* Function mergeFreeChunks()
@ -155,15 +140,6 @@ protected:
*/
void mergeFreeChunks();
/**
* Function getPowerOf2()
* returns the nearest power of 2, bigger than aNumber.
*
* @param aNumber is the number for which we look for a bigger power of 2.
*/
unsigned int getPowerOf2( unsigned int aNumber ) const;
private:
/**
* Function getChunkSize()
* returns size of the given chunk.
@ -192,6 +168,7 @@ private:
*/
void addFreeChunk( unsigned int aOffset, unsigned int aSize );
private:
/// Debug & test functions
void showFreeChunks();
void showUsedChunks();

View File

@ -0,0 +1,92 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef CACHED_CONTAINER_GPU_H_
#define CACHED_CONTAINER_GPU_H_
#include <gal/opengl/cached_container.h>
namespace KIGFX
{
/**
* @brief Specialization of CACHED_CONTAINER that stores data in video memory via memory mapping.
*/
class CACHED_CONTAINER_GPU : public CACHED_CONTAINER
{
public:
CACHED_CONTAINER_GPU( unsigned int aSize = DEFAULT_SIZE );
~CACHED_CONTAINER_GPU();
unsigned int GetBufferHandle() const override
{
return m_glBufferHandle;
}
bool IsMapped() const override
{
return m_isMapped;
}
///> @copydoc VERTEX_CONTAINER::Map()
void Map() override;
///> @copydoc VERTEX_CONTAINER::Unmap()
void Unmap() override;
protected:
///> Flag saying if vertex buffer is currently mapped
bool m_isMapped;
///> Flag saying if the vertex buffer is initialized
bool m_isInitialized;
///> Vertex buffer handle
unsigned int m_glBufferHandle;
///> Flag saying whether it is safe to use glCopyBufferSubData
bool m_useCopyBuffer;
/**
* Function init()
* performs the GL vertex buffer initialization. It can be invoked only when an OpenGL context
* is bound.
*/
void init();
/**
* Function defragmentResize()
* removes empty spaces between chunks and optionally resizes the container.
* After the operation there is continous space for storing vertices at the end of the container.
*
* @param aNewSize is the new size of container, expressed in number of vertices
* @return false in case of failure (e.g. memory shortage)
*/
bool defragmentResize( unsigned int aNewSize ) override;
bool defragmentResizeMemcpy( unsigned int aNewSize );
};
} // namespace KIGFX
#endif /* CACHED_CONTAINER_GPU_H_ */

View File

@ -0,0 +1,84 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef CACHED_CONTAINER_RAM_H_
#define CACHED_CONTAINER_RAM_H_
#include <gal/opengl/cached_container.h>
#include <map>
#include <set>
namespace KIGFX
{
class VERTEX_ITEM;
class SHADER;
/**
* @brief Specialization of CACHED_CONTAINER that stores data in RAM. This is mainly for
* video cards/drivers that do not cope well with video memory mapping.
*/
class CACHED_CONTAINER_RAM : public CACHED_CONTAINER
{
public:
CACHED_CONTAINER_RAM( unsigned int aSize = DEFAULT_SIZE );
~CACHED_CONTAINER_RAM();
///> @copydoc VERTEX_CONTAINER::Unmap()
void Map() override {}
///> @copydoc VERTEX_CONTAINER::Unmap()
void Unmap() override;
bool IsMapped() const override
{
return true;
}
/**
* Function GetBufferHandle()
* returns handle to the vertex buffer. It might be negative if the buffer is not initialized.
*/
unsigned int GetBufferHandle() const override
{
return m_verticesBuffer; // make common with CACHED_CONTAINER_RAM
}
protected:
///> Flag saying if the vertex buffer is initialized
bool m_isInitialized;
///> Handle to vertices buffer
GLuint m_verticesBuffer;
/**
* Defragments the currently stored data and resizes the buffer.
* @param aNewSize is the new buffer vertex buffer size, expressed as the number of vertices.
* @return true on success.
*/
bool defragmentResize( unsigned int aNewSize ) override;
};
} // namespace KIGFX
#endif /* CACHED_CONTAINER_RAM_H_ */

View File

@ -22,11 +22,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file gpu_manager.h
* @brief Class to handle uploading vertices and indices to GPU in drawing purposes.
*/
#ifndef GPU_MANAGER_H_
#define GPU_MANAGER_H_
@ -40,6 +35,10 @@ class VERTEX_CONTAINER;
class CACHED_CONTAINER;
class NONCACHED_CONTAINER;
/**
* @brief Class to handle uploading vertices and indices to GPU in drawing purposes.
*/
class GPU_MANAGER
{
public:

View File

@ -41,9 +41,14 @@ class SHADER;
class NONCACHED_CONTAINER : public VERTEX_CONTAINER
{
public:
NONCACHED_CONTAINER( unsigned int aSize = defaultInitSize );
NONCACHED_CONTAINER( unsigned int aSize = DEFAULT_SIZE );
virtual ~NONCACHED_CONTAINER();
bool IsCached() const override
{
return false;
}
/// @copydoc VERTEX_CONTAINER::SetItem( VERTEX_ITEM* aItem )
virtual void SetItem( VERTEX_ITEM* aItem ) override;

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -48,6 +48,12 @@ public:
virtual ~VERTEX_CONTAINER();
/**
* Returns true if the container caches vertex data in RAM or video memory.
* Otherwise it is a single batch draw which is later discarded.
*/
virtual bool IsCached() const = 0;
/**
* Function Map()
* prepares the container for vertices updates.
@ -58,8 +64,7 @@ public:
* Function Unmap()
* finishes the vertices updates stage.
*/
virtual void Unmap()
{}
virtual void Unmap() {}
/**
* Function SetItem()
@ -153,7 +158,7 @@ public:
}
protected:
VERTEX_CONTAINER( unsigned int aSize = defaultInitSize );
VERTEX_CONTAINER( unsigned int aSize = DEFAULT_SIZE );
///< How many vertices we can store in the container
unsigned int m_freeSpace;
@ -182,7 +187,7 @@ protected:
}
///< Default initial size of a container (expressed in vertices)
static const unsigned int defaultInitSize = 1048576;
static constexpr unsigned int DEFAULT_SIZE = 1048576;
};
} // namespace KIGFX

View File

@ -42,6 +42,7 @@ class VERTEX_ITEM
{
public:
friend class CACHED_CONTAINER;
friend class CACHED_CONTAINER_GPU;
friend class VERTEX_MANAGER;
explicit VERTEX_ITEM( const VERTEX_MANAGER& aManager );