From cd517f67db5195815272028f232e5e791d25cce0 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Tue, 18 Jun 2013 16:20:29 +0200 Subject: [PATCH] Added VBO_CONTAINER as a faster storage for vertices (OPENGL_GAL), tuned for exchanging data with GPU. Removed a few unnecessary variables and fields from OPENGL_GAL. Added function GAL::ClearCache() for freeing memory used by cached items. Fixed a few memory leaks (tesselator, PAINTER's settings & VIEW_ITEM's groups). Changed a few functions into inlines. --- common/CMakeLists.txt | 1 + common/gal/cairo/cairo_gal.cpp | 18 +- common/gal/opengl/opengl_gal.cpp | 312 ++++++++----------- common/gal/opengl/vbo_container.cpp | 369 +++++++++++++++++++++++ common/gal/opengl/vbo_item.cpp | 105 +++---- common/gal/stroke_font.cpp | 4 +- common/painter.cpp | 1 + common/view/view.cpp | 5 + common/view/view_item.cpp | 4 +- include/gal/cairo/cairo_gal.h | 3 + include/gal/graphics_abstraction_layer.h | 5 + include/gal/opengl/opengl_gal.h | 94 +++++- include/gal/opengl/vbo_container.h | 256 ++++++++++++++++ include/gal/opengl/vbo_item.h | 81 +++-- include/view/view_item.h | 3 +- 15 files changed, 925 insertions(+), 336 deletions(-) create mode 100644 common/gal/opengl/vbo_container.cpp create mode 100644 include/gal/opengl/vbo_container.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index cffff218b0..4a173cf3fa 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -21,6 +21,7 @@ set(GAL_SRCS gal/opengl/opengl_gal.cpp gal/opengl/shader.cpp gal/opengl/vbo_item.cpp + gal/opengl/vbo_container.cpp gal/cairo/cairo_gal.cpp view/wx_view_controls.cpp ) diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp index c2be24ce53..a9b898355d 100644 --- a/common/gal/cairo/cairo_gal.cpp +++ b/common/gal/cairo/cairo_gal.cpp @@ -99,10 +99,7 @@ CAIRO_GAL::~CAIRO_GAL() delete cursorPixels; delete cursorPixelsSaved; - for( int i = groups.size() - 1; i >= 0; --i ) - { - DeleteGroup( i ); - } + ClearCache(); deleteBitmaps(); } @@ -687,13 +684,22 @@ void CAIRO_GAL::EndGroup() } +void CAIRO_GAL::ClearCache() +{ + for( int i = groups.size() - 1; i >= 0; --i ) + { + DeleteGroup( i ); + } +} + + void CAIRO_GAL::DeleteGroup( int aGroupNumber ) { storePath(); // Delete the Cairo paths - for( std::deque::iterator it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end(); - it != end; ++it ) + std::deque::iterator it, end; + for( it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end(); it != end; ++it ) { if( it->command == CMD_FILL_PATH || it->command == CMD_STROKE_PATH ) { diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index cc20bbe6d9..33a36f1fb9 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -29,8 +29,8 @@ #include #include +#include #include -#include #include #include @@ -78,7 +78,6 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, isVboInitialized = false; vboNeedsUpdate = false; curVboItem = NULL; - vboSize = 0; transform = glm::mat4( 1.0f ); // Identity matrix SetSize( parentSize ); @@ -107,12 +106,26 @@ OPENGL_GAL::OPENGL_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener, Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) ); #endif + vboContainer = new VBO_CONTAINER; + + // Tesselator initialization + tesselator = gluNewTess(); + InitTesselatorCallbacks( tesselator ); + gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); + if( !isUseShader ) { + // (3 vertices per triangle) * (2 items [circle&semicircle]) * (number of points per item) + precomputedContainer = new VBO_CONTAINER( 3 * 2 * CIRCLE_POINTS ); + // Compute the unit circles, used for speed up of the circle drawing + verticesCircle = new VBO_ITEM( precomputedContainer ); computeUnitCircle(); + verticesCircle->Finish(); + + verticesSemiCircle = new VBO_ITEM( precomputedContainer ); computeUnitSemiCircle(); - //computeUnitArcs(); // TODO remove? it is not used anywhere + verticesSemiCircle->Finish(); } } @@ -121,6 +134,13 @@ OPENGL_GAL::~OPENGL_GAL() { glFlush(); + if( !isUseShader ) + { + delete verticesCircle; + delete verticesSemiCircle; + delete precomputedContainer; + } + // Delete the buffers if( isFrameBufferInitialized ) { @@ -128,16 +148,13 @@ OPENGL_GAL::~OPENGL_GAL() deleteFrameBuffer( &frameBufferBackup, &depthBufferBackup, &textureBackup ); } + gluDeleteTess( tesselator ); + if( isVboInitialized ) { - std::deque::iterator it, end; - - for( it = vboItems.begin(), end = vboItems.end(); it != end; it++ ) - { - delete *it; - } - + ClearCache(); deleteVertexBufferObjects(); + delete vboContainer; } delete glContext; @@ -241,8 +258,8 @@ void OPENGL_GAL::initFrameBuffers() void OPENGL_GAL::initVertexBufferObjects() { // Generate buffers for vertices and indices - glGenBuffers( 1, &curVboVertId ); - glGenBuffers( 1, &curVboIndId ); + glGenBuffers( 1, &vboVertices ); + glGenBuffers( 1, &vboIndices ); isVboInitialized = true; } @@ -253,8 +270,8 @@ void OPENGL_GAL::deleteVertexBufferObjects() glBindBuffer( GL_ARRAY_BUFFER, 0 ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); - glDeleteBuffers( 1, &curVboVertId ); - glDeleteBuffers( 1, &curVboIndId ); + glDeleteBuffers( 1, &vboVertices ); + glDeleteBuffers( 1, &vboIndices ); isVboInitialized = false; } @@ -348,7 +365,6 @@ void OPENGL_GAL::BeginDrawing() // Compile the shaders if( !isShaderInitialized && isUseShader ) { - shader.ConfigureGeometryShader( 3, GL_TRIANGLES, GL_TRIANGLES ); shader.AddSource( shaderPath + std::string( "/shader.vert" ), SHADER_TYPE_VERTEX ); shader.AddSource( shaderPath + std::string( "/shader.frag" ), SHADER_TYPE_FRAGMENT ); if( !shader.Link() ) @@ -417,9 +433,10 @@ void OPENGL_GAL::BeginDrawing() // Number of vertices to be drawn indicesSize = 0; - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, curVboIndId ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vboIndices ); // Discard old buffer, so we can use it again - glBufferData( GL_ELEMENT_ARRAY_BUFFER, vboSize * VBO_ITEM::IndByteSize, NULL, GL_STREAM_DRAW ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, vboContainer->GetSize() * VBO_ITEM::IndByteSize, + NULL, GL_STREAM_DRAW ); // Map the GPU memory, so we can store indices that are going to be drawn indicesPtr = static_cast( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) ); @@ -493,7 +510,7 @@ void OPENGL_GAL::EndDrawing() glEnableClientState( GL_COLOR_ARRAY ); // Bind vertices data buffers - glBindBuffer( GL_ARRAY_BUFFER, curVboVertId ); + glBindBuffer( GL_ARRAY_BUFFER, vboVertices ); glVertexPointer( VBO_ITEM::CoordStride, GL_FLOAT, VBO_ITEM::VertByteSize, 0 ); glColorPointer( VBO_ITEM::ColorStride, GL_FLOAT, VBO_ITEM::VertByteSize, (GLvoid*) VBO_ITEM::ColorByteOffset ); @@ -544,40 +561,27 @@ void OPENGL_GAL::rebuildVbo() prof_start( &totalTime, false ); #endif /* __WXDEBUG__ */ - // Buffer for storing cached items data - GLfloat* verticesBuffer = new GLfloat[VBO_ITEM::VertStride * vboSize]; + GLfloat* data = (GLfloat*) vboContainer->GetAllVertices(); - // Pointer for easier usage with memcpy - GLfloat* verticesBufferPtr = verticesBuffer; - - // Fill out buffers with data - for( std::deque::iterator vboItem = vboItems.begin(); - vboItem != vboItems.end(); vboItem++ ) - { - int size = (*vboItem)->GetSize(); - - memcpy( verticesBufferPtr, (*vboItem)->GetVertices(), size * VBO_ITEM::VertByteSize ); - verticesBufferPtr += size * VBO_ITEM::VertStride; - } - - // Upload vertices coordinates, shader types and indices to GPU memory - glBindBuffer( GL_ARRAY_BUFFER, curVboVertId ); - glBufferData( GL_ARRAY_BUFFER, vboSize * VBO_ITEM::VertByteSize, verticesBuffer, GL_DYNAMIC_DRAW ); + // Upload vertices coordinates and shader types to GPU memory + glBindBuffer( GL_ARRAY_BUFFER, vboVertices ); + glBufferData( GL_ARRAY_BUFFER, vboContainer->GetSize() * VBO_ITEM::VertByteSize, + data, GL_DYNAMIC_DRAW ); glBindBuffer( GL_ARRAY_BUFFER, 0 ); // Allocate the biggest possible buffer for indices - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, curVboIndId ); - glBufferData( GL_ELEMENT_ARRAY_BUFFER, vboSize * VBO_ITEM::IndByteSize, NULL, GL_STREAM_DRAW ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vboIndices ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, vboContainer->GetSize() * VBO_ITEM::IndByteSize, + NULL, GL_STREAM_DRAW ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); - delete[] verticesBuffer; vboNeedsUpdate = false; #ifdef __WXDEBUG__ prof_end( &totalTime ); wxLogDebug( wxT( "Rebuilding VBO::%d vertices / %.1f ms" ), - vboSize, (double) totalTime.value / 1000.0 ); + vboContainer->GetSize(), (double) totalTime.value / 1000.0 ); #endif /* __WXDEBUG__ */ } @@ -726,74 +730,6 @@ inline void OPENGL_GAL::drawLineCap( const VECTOR2D& aStartPoint, const VECTOR2D } -void OPENGL_GAL::begin( GLenum aMode ) -{ - if( !isGrouping ) - glBegin( aMode ); -} - - -void OPENGL_GAL::end() -{ - if( !isGrouping ) - glEnd(); -} - - -void OPENGL_GAL::vertex3( double aX, double aY, double aZ ) -{ - if( isGrouping ) - { - // New vertex coordinates for VBO - const GLfloat vertex[] = { aX, aY, aZ }; - curVboItem->PushVertex( vertex ); - } - else - { - glVertex3d( aX, aY, aZ ); - } -} - - -void OPENGL_GAL::translate3( double aX, double aY, double aZ ) -{ - if( isGrouping ) - { - transform = glm::translate( transform, glm::vec3( aX, aY, aZ ) ); - } - else - { - glTranslated( aX, aY, aZ ); - } -} - - -void OPENGL_GAL::color4( double aRed, double aGreen, double aBlue, double aAlpha ) -{ - if( isGrouping ) - { - curVboItem->UseColor( COLOR4D( aRed, aGreen, aBlue, aAlpha ) ); - } - else - { - glColor4d( aRed, aGreen, aBlue, aAlpha ); - } -} - - -void OPENGL_GAL::color4( const COLOR4D& aColor ) -{ - if( isGrouping ) - { - curVboItem->UseColor( aColor ); - } - else - { - glColor4d( aColor.r, aColor.g, aColor.b, aColor.a); - } -} - - void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) { if( isFillEnabled ) @@ -1206,7 +1142,13 @@ void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) if( isGrouping ) { - curVboItem->PushVertices( verticesCircle.GetVertices(), verticesCircle.GetSize() ); + VBO_VERTEX* circle = new VBO_VERTEX[CIRCLE_POINTS * 3]; + + memcpy( circle, verticesCircle->GetVertices(), + VBO_ITEM::VertByteSize * CIRCLE_POINTS * 3 ); + curVboItem->PushVertices( circle, CIRCLE_POINTS * 3 ); + + delete[] circle; } else { @@ -1256,7 +1198,13 @@ void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, d if( isGrouping ) { - curVboItem->PushVertices( verticesSemiCircle.GetVertices(), verticesSemiCircle.GetSize() ); + VBO_VERTEX* semiCircle = new VBO_VERTEX[CIRCLE_POINTS * 3]; + + memcpy( semiCircle, verticesSemiCircle->GetVertices(), + VBO_ITEM::VertByteSize * CIRCLE_POINTS * 3 ); + curVboItem->PushVertices( semiCircle, CIRCLE_POINTS * 3 ); + + delete[] semiCircle; } else { @@ -1416,32 +1364,19 @@ void OPENGL_GAL::DrawPolygon( const std::deque& aPointList ) setShader( SHADER_NONE ); - GLUtesselator* tesselator = gluNewTess(); - typedef std::vector OGLPOINTS; // Do only one heap allocation, can do because we know size in advance. // std::vector is then fastest OGLPOINTS vertexList( aPointList.size(), OGLPOINT( "fastest" ) ); - InitTesselatorCallbacks( tesselator ); - - gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); - glNormal3d( 0.0, 0.0, 1.0 ); color4( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); glShadeModel( GL_FLAT ); - if( isGrouping ) - { - // Store polygon triangles' coordinates to the current VBO item - gluTessBeginPolygon( tesselator, curVboItem ); - } - else - { - // Display polygons directly - gluTessBeginPolygon( tesselator, NULL ); - } + + TessParams params = { curVboItem, tessIntersects }; + gluTessBeginPolygon( tesselator, ¶ms ); gluTessBeginContour( tesselator ); // use operator=( const POINTS& ) @@ -1456,7 +1391,13 @@ void OPENGL_GAL::DrawPolygon( const std::deque& aPointList ) gluTessEndContour( tesselator ); gluTessEndPolygon( tesselator ); - gluDeleteTess( tesselator ); + // Free allocated intersecting points + std::vector::iterator it, it_end; + for( it = tessIntersects.begin(), it_end = tessIntersects.end(); it < it_end; ++it ) + { + delete[] *it; + } + tessIntersects.clear(); // vertexList destroyed here } @@ -1639,8 +1580,7 @@ int OPENGL_GAL::BeginGroup() vboNeedsUpdate = true; // Save the pointer for caching the current item - curVboItem = new VBO_ITEM; - curVboItem->SetOffset( vboSize ); + curVboItem = new VBO_ITEM( vboContainer ); vboItems.push_back( curVboItem ); return vboItems.size() - 1; @@ -1649,27 +1589,30 @@ int OPENGL_GAL::BeginGroup() void OPENGL_GAL::EndGroup() { - vboSize += curVboItem->GetSize(); + curVboItem->Finish(); curVboItem = NULL; isGrouping = false; } +void OPENGL_GAL::ClearCache() +{ + std::deque::iterator it, end; + for( it = vboItems.begin(), end = vboItems.end(); it != end; it++ ) + { + delete *it; + } + + vboItems.clear(); +} + + void OPENGL_GAL::DeleteGroup( int aGroupNumber ) { -#ifdef __WXDEBUG__ - if( (unsigned) aGroupNumber < vboItems.size() ) - { - wxLogDebug( wxT( "Tried to delete not existing group" ) ); - } -#endif /* __WXDEBUG__ */ + VBO_ITEM* item = vboItems[aGroupNumber]; - std::deque::iterator it = vboItems.begin(); - std::advance( it, aGroupNumber ); - - // vboSize -= it->GetSize(); // FIXME? - delete *it; - // vboItems.erase( it ); // makes change to group numbers - that's veeery bad + vboItems[aGroupNumber] = NULL; + delete item; vboNeedsUpdate = true; } @@ -1687,26 +1630,6 @@ void OPENGL_GAL::DrawGroup( int aGroupNumber ) } -// TODO it is not used anywhere -/*void OPENGL_GAL::computeUnitArcs() -{ - displayListsArcs = glGenLists( CIRCLE_POINTS + 1 ); - - // Create an individual display list for each arc in with an angle [0 .. 2pi] - for( int j = 0; j < CIRCLE_POINTS + 1; j++ ) - { - glNewList( displayListsArcs + j, GL_COMPILE ); - - for( int i = 0; i < j; i++ ) - { - glVertex2d( cos( 2 * M_PI / CIRCLE_POINTS * i ), sin( 2 * M_PI / CIRCLE_POINTS * i ) ); - } - - glEndList(); - } -}*/ - - void OPENGL_GAL::computeUnitCircle() { displayListCircle = glGenLists( 1 ); @@ -1718,30 +1641,28 @@ void OPENGL_GAL::computeUnitCircle() // Insert in a display list and a vector for( int i = 0; i < CIRCLE_POINTS; i++ ) { - const GLfloat v0[] = { 0.0f, 0.0f, 0.0f }; - const GLfloat v1[] = - { + VBO_VERTEX v0( 0.0f, 0.0f, 0.0f ); + VBO_VERTEX v1( cos( 2.0 * M_PI / CIRCLE_POINTS * i ), // x sin( 2.0 * M_PI / CIRCLE_POINTS * i ), // y 0.0f // z - }; - const GLfloat v2[] = - { + ); + VBO_VERTEX v2( cos( 2.0 * M_PI / CIRCLE_POINTS * ( i + 1 ) ), // x sin( 2.0 * M_PI / CIRCLE_POINTS * ( i + 1 ) ), // y 0.0f // z - }; + ); glVertex2d( 0, 0 ); - verticesCircle.PushVertex( v0 ); + verticesCircle->PushVertex( &v0 ); - glVertex2d( v1[0], v1[1] ); - verticesCircle.PushVertex( v1 ); - unitCirclePoints.push_back( VECTOR2D( v1[0], v1[1] ) ); // TODO remove + glVertex2d( v1.x, v1.y ); + verticesCircle->PushVertex( &v1 ); + unitCirclePoints.push_back( VECTOR2D( v1.x, v1.y ) ); // TODO remove - glVertex2d( v2[0], v2[1] ); - verticesCircle.PushVertex( v2 ); - unitCirclePoints.push_back( VECTOR2D( v2[0], v2[1] ) ); // TODO remove + glVertex2d( v2.x, v2.y ); + verticesCircle->PushVertex( &v2 ); + unitCirclePoints.push_back( VECTOR2D( v2.x, v2.y ) ); // TODO remove } glEnd(); @@ -1759,28 +1680,26 @@ void OPENGL_GAL::computeUnitSemiCircle() for( int i = 0; i < CIRCLE_POINTS / 2; ++i ) { - GLfloat v0[] = { 0.0f, 0.0f, 0.0f }; - GLfloat v1[] = - { + VBO_VERTEX v0( 0.0f, 0.0f, 0.0f ); + VBO_VERTEX v1( cos( 2.0 * M_PI / CIRCLE_POINTS * i ), // x sin( 2.0 * M_PI / CIRCLE_POINTS * i ), // y 0.0f // z - }; - GLfloat v2[] = - { + ); + VBO_VERTEX v2( cos( 2.0 * M_PI / CIRCLE_POINTS * ( i + 1 ) ), // x sin( 2.0 * M_PI / CIRCLE_POINTS * ( i + 1 ) ), // y 0.0f // z - }; + ); glVertex2d( 0, 0 ); - verticesSemiCircle.PushVertex( v0 ); + verticesSemiCircle->PushVertex( &v0 ); - glVertex2d( v1[0], v1[1] ); - verticesSemiCircle.PushVertex( v1 ); + glVertex2d( v1.x, v1.y ); + verticesSemiCircle->PushVertex( &v1 ); - glVertex2d( v2[0], v2[1] ); - verticesSemiCircle.PushVertex( v2 ); + glVertex2d( v2.x, v2.y ); + verticesSemiCircle->PushVertex( &v2 ); } glEnd(); @@ -1825,12 +1744,13 @@ void OPENGL_GAL::ComputeWorldScreenMatrix() void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData ) { GLdouble* vertex = static_cast( aVertexPtr ); + OPENGL_GAL::TessParams* param = static_cast( aData ); + VBO_ITEM* vboItem = param->vboItem; - if( aData ) + if( vboItem ) { - VBO_ITEM* vboItem = static_cast( aData ); - const GLfloat newVertex[] = { vertex[0], vertex[1], vertex[2] }; - vboItem->PushVertex( newVertex ); + VBO_VERTEX newVertex( vertex[0], vertex[1], vertex[2] ); + vboItem->PushVertex( &newVertex ); } else { @@ -1841,9 +1761,13 @@ void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData ) void CALLBACK CombineCallback( GLdouble coords[3], GLdouble* vertex_data[4], - GLfloat weight[4], GLdouble** dataOut ) + GLfloat weight[4], GLdouble** dataOut, void* aData ) { GLdouble* vertex = new GLdouble[3]; + OPENGL_GAL::TessParams* param = static_cast( aData ); + + // Save the pointer so we can delete it later + param->intersectPoints.push_back( vertex ); memcpy( vertex, coords, 3 * sizeof(GLdouble) ); @@ -1884,7 +1808,7 @@ void CALLBACK ErrorCallback( GLenum aErrorCode ) void InitTesselatorCallbacks( GLUtesselator* aTesselator ) { gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback ); - gluTessCallback( aTesselator, GLU_TESS_COMBINE, ( void (CALLBACK*)() )CombineCallback ); + gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback ); gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, ( void (CALLBACK*)() )EdgeCallback ); gluTessCallback( aTesselator, GLU_TESS_BEGIN_DATA, ( void (CALLBACK*)() )BeginCallback ); gluTessCallback( aTesselator, GLU_TESS_END_DATA, ( void (CALLBACK*)() )EndCallback ); diff --git a/common/gal/opengl/vbo_container.cpp b/common/gal/opengl/vbo_container.cpp new file mode 100644 index 0000000000..33fd141a4a --- /dev/null +++ b/common/gal/opengl/vbo_container.cpp @@ -0,0 +1,369 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * 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 + */ + +/** + * @file vbo_container.cpp + * @brief Class to store VBO_ITEMs. + */ + +#include +#include +#include +#include +#ifdef __WXDEBUG__ +#include +#endif /* __WXDEBUG__ */ + +#define CONTAINER_TEST 1 + +using namespace KiGfx; + +VBO_CONTAINER::VBO_CONTAINER( int aSize ) : + m_freeSpace( aSize ), m_currentSize( aSize ), itemStarted( false ) +{ + m_vertices = new VBO_VERTEX[aSize]; + + // In the beginning there is only free space + m_freeChunks.insert( Chunk( aSize, 0 ) ); +} + + +VBO_CONTAINER::~VBO_CONTAINER() +{ + delete[] m_vertices; +} + + +void VBO_CONTAINER::StartItem( VBO_ITEM* aVboItem ) +{ + itemStarted = true; + item = aVboItem; + itemSize = 0; + + // Reserve minimal sensible chunk size (at least to store a single triangle) + itemChunkSize = 3; + allocate( aVboItem, itemChunkSize ); +} + + +void VBO_CONTAINER::EndItem() +{ + if( itemSize < itemChunkSize ) + { + // There is some memory left, so we should return it to the pool + int itemChunkOffset = item->GetOffset(); + + m_reservedChunks.erase( item ); + m_reservedChunks.insert( ReservedChunk( item, Chunk( itemSize, itemChunkOffset ) ) ); + + m_freeChunks.insert( Chunk( itemChunkSize - itemSize, itemChunkOffset + itemSize ) ); + m_freeSpace += ( itemChunkSize - itemSize ); + } + + itemStarted = false; +} + + +void VBO_CONTAINER::Add( VBO_ITEM* aVboItem, const VBO_VERTEX* aVertex, unsigned int aSize ) +{ + unsigned int offset; + + if( itemStarted ) // There is an item being created with an unknown size.. + { + unsigned int itemChunkOffset; + + // ..and unfortunately does not fit into currently reserved chunk + if( itemSize + aSize > itemChunkSize ) + { + // Find the previous chunk for the item, save it, so it can be removed later + ReservedChunkMap::iterator it = m_reservedChunks.find( item ); + + // Reserve bigger memory for the current item + int newSize = ( 2 * itemSize ) + aSize; + itemChunkOffset = allocate( aVboItem, newSize ); + + // Check if there was no error + if( itemChunkOffset > m_currentSize ) + return; + + // Save previous chunk's offset for copying data + int oldItemChunkOffset = getChunkOffset( *it ); + + // Copy all the old data + memcpy( &m_vertices[itemChunkOffset], &m_vertices[oldItemChunkOffset], + itemSize * VBO_ITEM::VertByteSize ); + + // Return memory used by the previous chunk + free( it ); + + itemChunkSize = newSize; + } + else + { + itemChunkOffset = item->GetOffset(); + } + + // Store new vertices in the chunk reserved for the unknown-sized item + offset = itemChunkOffset + itemSize; + itemSize += aSize; + } + else + { + // Add vertices to previously already finished item + wxASSERT_MSG( false, wxT( "Warning: not tested yet" ) ); + + ReservedChunkMap::iterator it = m_reservedChunks.find( aVboItem ); + unsigned int chunkSize = getChunkSize( *it ); + unsigned int itemSize = aVboItem->GetSize(); + + if( chunkSize < itemSize + aSize ) + { + resizeChunk( aVboItem, itemSize + aSize ); + it = m_reservedChunks.find( aVboItem ); + } + + offset = getChunkOffset( *it ) + itemSize; + } + + memcpy( &m_vertices[offset], aVertex, aSize * VBO_ITEM::VertByteSize ); +} + + +VBO_VERTEX* VBO_CONTAINER::GetAllVertices() const +{ + return m_vertices; +} + + +VBO_VERTEX* VBO_CONTAINER::GetVertices( const VBO_ITEM* aVboItem ) const +{ + int offset = aVboItem->GetOffset(); + + return &m_vertices[offset]; +} + + +unsigned int VBO_CONTAINER::allocate( VBO_ITEM* aVboItem, unsigned int aSize ) +{ + // Is there enough space to store vertices? + if( m_freeSpace < aSize ) + { + bool result; + + // Would it be enough to double the current space? + if( aSize < m_freeSpace + m_currentSize ) + { + // Yes: exponential growing + result = resizeContainer( m_currentSize * 2 ); + } + else + { + // No: grow to the nearest bigger power of 2 + result = resizeContainer( getPowerOf2( m_currentSize * 2 + aSize ) ); + } + + // An error has occurred + if( !result ) + return UINT_MAX; + } + + // Look for the space with at least given size + FreeChunkMap::iterator it = m_freeChunks.lower_bound( aSize ); + + if( it == m_freeChunks.end() ) + { + // This means that there is enough space for + // storing vertices, but the space is not continous + if( !defragment() ) + return false; + + // We can take the first free chunk, as there is only one after defragmentation + // and we can be sure that it provides enough space to store the object + it = m_freeChunks.begin(); + } + + unsigned int chunkSize = it->first; + unsigned int chunkOffset = it->second; + m_freeChunks.erase( it ); + + wxASSERT( chunkSize >= aSize ); + + // If there is some space left, return it to the pool - add an entry for it + if( chunkSize > aSize ) + { + m_freeChunks.insert( Chunk( chunkSize - aSize, chunkOffset + aSize ) ); + } + m_freeSpace -= aSize; + m_reservedChunks.insert( ReservedChunk( aVboItem, Chunk( aSize, chunkOffset ) ) ); + + aVboItem->SetOffset( chunkOffset ); + + return chunkOffset; +} + + +void VBO_CONTAINER::free( const ReservedChunkMap::iterator& aChunk ) +{ + // Remove the chunk from the reserved chunks map and add to the free chunks map + int size = getChunkSize( *aChunk ); + int offset = getChunkOffset( *aChunk ); + + m_reservedChunks.erase( aChunk ); + m_freeChunks.insert( Chunk( size, offset ) ); + m_freeSpace += size; +} + + +bool VBO_CONTAINER::defragment( VBO_VERTEX* aTarget ) +{ + if( m_freeChunks.size() <= 1 ) + { + // There is no point in defragmenting, as there is only one or no free chunks + return true; + } + + if( aTarget == NULL ) + { + // No target was specified, so we have to allocate our own space + aTarget = new (std::nothrow) VBO_VERTEX[m_currentSize]; + if( aTarget == NULL ) + { + wxLogError( wxT( "Run out of memory" ) ); + return false; + } + } + + int newOffset = 0; + ReservedChunkMap::iterator it, it_end; + for( it = m_reservedChunks.begin(), it_end = m_reservedChunks.end(); it != it_end; ++it ) + { + VBO_ITEM* vboItem = getChunkVboItem( *it ); + int itemOffset = getChunkOffset( *it ); + int itemSize = getChunkSize( *it ); + + // Move an item to the new container + memcpy( &aTarget[newOffset], &m_vertices[itemOffset], itemSize * VBO_ITEM::VertByteSize ); + + // Update new offset + vboItem->SetOffset( newOffset ); + setChunkOffset( *it, newOffset ); + + // Move to the next free space + newOffset += itemSize; + } + + delete[] m_vertices; + m_vertices = aTarget; + + // Now there is only one big chunk of free memory + m_freeChunks.clear(); + m_freeChunks.insert( Chunk( m_freeSpace, m_currentSize - m_freeSpace ) ); + + return true; +} + + +void VBO_CONTAINER::resizeChunk( VBO_ITEM* aVboItem, int aNewSize ) +{ + wxASSERT_MSG( false, wxT( "Warning: not tested yet" ) ); + + // TODO ESPECIALLY test the case of shrinking chunk + ReservedChunkMap::iterator it = m_reservedChunks.find( aVboItem ); + int size = getChunkSize( *it ); + int offset = getChunkOffset( *it ); + + int newOffset = allocate( aVboItem, aNewSize ); + memcpy( &m_vertices[newOffset], &m_vertices[offset], size * VBO_ITEM::VertByteSize ); + + // Remove the chunk from the reserved chunks map and add to the free chunks map + m_reservedChunks.erase( it ); + m_freeChunks.insert( Chunk( size, offset ) ); + m_freeSpace += size; +} + + +bool VBO_CONTAINER::resizeContainer( unsigned int aNewSize ) +{ + unsigned int copySize; + if( aNewSize < m_currentSize ) + { + // Sanity check, no shrinking if we cannot fit all the data + if( ( m_currentSize - m_freeSpace ) > aNewSize ) + return false; + + defragment(); + copySize = ( m_currentSize - m_freeSpace ); + } + else + { + copySize = m_currentSize; + } + + VBO_VERTEX* newContainer = new (std::nothrow) VBO_VERTEX[aNewSize]; + if( newContainer == NULL ) + { + wxLogError( wxT( "Run out of memory" ) ); + return false; + } + + memcpy( newContainer, m_vertices, copySize * VBO_ITEM::VertByteSize ); + delete[] m_vertices; + m_vertices = newContainer; + + // Update variables + unsigned int lastFreeSize = 0; + unsigned int lastFreeOffset = 0; + + // Search for the last free chunk *at the end of the container* (not the last chunk in general) + FreeChunkMap::reverse_iterator lastFree, freeEnd; + for( lastFree = m_freeChunks.rbegin(), freeEnd = m_freeChunks.rend(); + lastFree != freeEnd && lastFreeSize + lastFreeOffset != m_currentSize; ++lastFree ) + { + lastFreeSize = getChunkSize( *lastFree ); + lastFreeOffset = getChunkOffset( *lastFree ); + } + + if( lastFreeSize + lastFreeOffset == m_currentSize ) + { + // We found a chunk at the end of the container + m_freeChunks.erase( lastFree.base() ); + // so we can merge it with the new free chunk + m_freeChunks.insert( Chunk( aNewSize - m_currentSize + lastFreeSize, // size + m_currentSize - lastFreeSize ) ); // offset + } + else + { + // As there is no free chunk at the end of container - simply add a new entry + if( aNewSize > m_currentSize ) // only in the case of enlargement + { + m_freeChunks.insert( Chunk( aNewSize - m_currentSize, // size + m_currentSize ) ); // offset + } + } + + m_freeSpace += ( aNewSize - m_currentSize ); + m_currentSize = aNewSize; + + return true; +} diff --git a/common/gal/opengl/vbo_item.cpp b/common/gal/opengl/vbo_item.cpp index 1412a83b57..8991c669af 100644 --- a/common/gal/opengl/vbo_item.cpp +++ b/common/gal/opengl/vbo_item.cpp @@ -27,107 +27,98 @@ * @brief Class to handle an item held in a Vertex Buffer Object. */ +#include #include #include using namespace KiGfx; -VBO_ITEM::VBO_ITEM() : - m_vertices( NULL ), +VBO_ITEM::VBO_ITEM( VBO_CONTAINER* aContainer ) : m_offset( 0 ), m_size( 0 ), + m_container( aContainer ), m_isDirty( true ), m_transform( NULL ) { // By default no shader is used m_shader[0] = 0; - // Prepare a block for storing vertices & indices - useNewBlock(); + // The item's size is not known yet, so we just start an item in the container + aContainer->StartItem( this ); } VBO_ITEM::~VBO_ITEM() { - if( m_isDirty ) - { - // Data is still stored in blocks - std::list::const_iterator v_it, v_end; - for( v_it = m_vertBlocks.begin(), v_end = m_vertBlocks.end(); v_it != v_end; ++v_it ) - delete[] *v_it; - } - - if( m_vertices ) - delete m_vertices; + m_container->Free( this ); } -void VBO_ITEM::PushVertex( const GLfloat* aVertex ) +void VBO_ITEM::PushVertex( VBO_VERTEX* aVertex ) { - if( m_spaceLeft == 0 ) - useNewBlock(); - if( m_transform != NULL ) { // Apply transformations - // X, Y, Z coordinates - glm::vec4 vertex( aVertex[0], aVertex[1], aVertex[2], 1.0f ); + glm::vec4 vertex( aVertex->x, aVertex->y, aVertex->z, 1.0f ); vertex = *m_transform * vertex; // Replace only coordinates, leave color as it is - memcpy( &m_vertPtr->x, &vertex[0], CoordByteSize ); - } - else - { - // Add the new vertex - memcpy( &m_vertPtr->x, aVertex, CoordByteSize ); + aVertex->x = vertex.x; + aVertex->y = vertex.y; + aVertex->z = vertex.z; } // Apply currently used color - memcpy( &m_vertPtr->r, m_color, ColorByteSize ); + aVertex->r = m_color[0]; + aVertex->g = m_color[1]; + aVertex->b = m_color[2]; + aVertex->a = m_color[3]; // Apply currently used shader - memcpy( &m_vertPtr->shader, m_shader, ShaderByteSize ); + for( int i = 0; i < ShaderStride; ++i ) + { + aVertex->shader[i] = m_shader[i]; + } - // Move to the next free space - m_vertPtr++; + m_container->Add( this, aVertex ); m_size++; m_isDirty = true; - m_spaceLeft--; } -void VBO_ITEM::PushVertices( const GLfloat* aVertices, GLuint aSize ) +void VBO_ITEM::PushVertices( VBO_VERTEX* aVertices, GLuint aSize ) { for( unsigned int i = 0; i < aSize; ++i ) { - PushVertex( &aVertices[i * VertStride] ); + PushVertex( &aVertices[i] ); } } -GLfloat* VBO_ITEM::GetVertices() +VBO_VERTEX* VBO_ITEM::GetVertices() { if( m_isDirty ) - prepareFinal(); + Finish(); - return m_vertices; + return m_container->GetVertices( this ); } void VBO_ITEM::ChangeColor( const COLOR4D& aColor ) { + wxASSERT_MSG( false, wxT( "This was not tested yet" ) ); + if( m_isDirty ) - prepareFinal(); + Finish(); // Point to color of vertices - GLfloat* vertexPtr = m_vertices + ColorOffset; + VBO_VERTEX* vertexPtr = GetVertices(); const GLfloat newColor[] = { aColor.r, aColor.g, aColor.b, aColor.a }; - for( int i = 0; i < m_size; ++i ) + for( unsigned int i = 0; i < m_size; ++i ) { - memcpy( vertexPtr, newColor, ColorByteSize ); + memcpy( &vertexPtr->r, newColor, ColorByteSize ); // Move on to the next vertex vertexPtr++; @@ -135,38 +126,10 @@ void VBO_ITEM::ChangeColor( const COLOR4D& aColor ) } -void VBO_ITEM::useNewBlock() +void VBO_ITEM::Finish() { - VBO_VERTEX* newVertBlock = new VBO_VERTEX[BLOCK_SIZE]; - - m_vertPtr = newVertBlock; - m_vertBlocks.push_back( newVertBlock ); - - m_spaceLeft = BLOCK_SIZE; -} - - -void VBO_ITEM::prepareFinal() -{ - if( m_vertices ) - delete m_vertices; - - // Allocate memory that would store all of vertices - m_vertices = new GLfloat[m_size * VertStride]; - // Set the pointer that will move along the buffer - GLfloat* vertPtr = m_vertices; - - // Copy blocks of vertices one after another to m_vertices - std::list::const_iterator v_it; - for( v_it = m_vertBlocks.begin(); *v_it != m_vertBlocks.back(); ++v_it ) - { - memcpy( vertPtr, *v_it, BLOCK_SIZE * VertByteSize ); - delete[] *v_it; - vertPtr += ( BLOCK_SIZE * VertStride ); - } - - // In the last block we need to copy only used vertices - memcpy( vertPtr, *v_it, ( BLOCK_SIZE - m_spaceLeft ) * VertByteSize ); + // The unknown-sized item has just ended, so we need to inform the container about it + m_container->EndItem(); m_isDirty = false; } diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp index 8125b73a04..b4b89965de 100644 --- a/common/gal/stroke_font.cpp +++ b/common/gal/stroke_font.cpp @@ -66,8 +66,8 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe while( aNewStrokeFont[j][i] ) { - VECTOR2D point; - char coordinate[2]; + VECTOR2D point( 0.0, 0.0 ); + char coordinate[2] = { 0, }; for( int k = 0; k < 2; k++ ) { diff --git a/common/painter.cpp b/common/painter.cpp index fa28295158..0541cb1e0e 100644 --- a/common/painter.cpp +++ b/common/painter.cpp @@ -79,6 +79,7 @@ PAINTER::PAINTER( GAL* aGal ) : PAINTER::~PAINTER() { delete m_stroke_font; + delete m_settings; } diff --git a/common/view/view.cpp b/common/view/view.cpp index 26188e3d86..33f83527f3 100644 --- a/common/view/view.cpp +++ b/common/view/view.cpp @@ -473,6 +473,11 @@ void VIEW::Clear() l->items->RemoveAll(); } + + if( m_useGroups ) + { + m_gal->ClearCache(); + } } diff --git a/common/view/view_item.cpp b/common/view/view_item.cpp index b7f680252d..266f7df186 100644 --- a/common/view/view_item.cpp +++ b/common/view/view_item.cpp @@ -111,7 +111,7 @@ void VIEW_ITEM::setGroup( int aLayer, int aId ) if( m_groupsSize > 0 ) { std::copy( m_groups, m_groups + m_groupsSize, newGroups ); - delete m_groups; + delete[] m_groups; } m_groups = newGroups; @@ -123,7 +123,7 @@ void VIEW_ITEM::deleteGroups() { if( m_groupsSize > 0 ) { - delete m_groups; + delete[] m_groups; m_groupsSize = 0; } } diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h index f70dd3e7c2..f042e962eb 100644 --- a/include/gal/cairo/cairo_gal.h +++ b/include/gal/cairo/cairo_gal.h @@ -212,6 +212,9 @@ public: /// @copydoc GAL::DeleteGroup() virtual void DeleteGroup( int aGroupNumber ); + /// @copydoc GAL::ClearCache() + virtual void ClearCache(); + // -------------------------------------------------------- // Handling the world <-> screen transformation // -------------------------------------------------------- diff --git a/include/gal/graphics_abstraction_layer.h b/include/gal/graphics_abstraction_layer.h index 245508b7a2..79bc8e2a8e 100644 --- a/include/gal/graphics_abstraction_layer.h +++ b/include/gal/graphics_abstraction_layer.h @@ -370,6 +370,11 @@ public: */ virtual void DeleteGroup( int aGroupNumber ) = 0; + /** + * @brief Delete all data created during caching of graphic items. + */ + virtual void ClearCache() = 0; + // -------------------------------------------------------- // Handling the world <-> screen transformation // -------------------------------------------------------- diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index 0c04447040..1dca06f2a0 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -35,6 +35,8 @@ // OpenGL mathematics library #define GLM_FORCE_RADIANS +#include + #include #include @@ -56,7 +58,7 @@ namespace KiGfx { class SHADER; -class VBO_ITEM; +class VBO_CONTAINER; /** * @brief Class OpenGL_GAL is the OpenGL implementation of the Graphics Abstraction Layer. @@ -236,6 +238,9 @@ public: /// @copydoc GAL::DeleteGroup() virtual void DeleteGroup( int aGroupNumber ); + /// @copydoc GAL::ClearCache() + virtual void ClearCache(); + // -------------------------------------------------------- // Handling the world <-> screen transformation // -------------------------------------------------------- @@ -315,6 +320,13 @@ public: shaderPath = aPath; } + ///< Parameters passed to the GLU tesselator + typedef struct + { + VBO_ITEM* vboItem; ///< VBO_ITEM for storing new vertices + std::vector& intersectPoints; ///< Intersect points, that have to be freed + } TessParams; + protected: virtual void DrawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ); @@ -336,20 +348,19 @@ private: wxEvtHandler* mouseListener; wxEvtHandler* paintListener; - // Display lists - GLuint displayListsArcs; ///< Arc display list - VBO_ITEM verticesArc; + // Display lists (used in shaderless mode) + VBO_CONTAINER* precomputedContainer; ///< Container for storing display lists GLuint displayListCircle; ///< Circle display list - VBO_ITEM verticesCircle; + VBO_ITEM* verticesCircle; GLuint displayListSemiCircle; ///< Semi circle display list - VBO_ITEM verticesSemiCircle; + VBO_ITEM* verticesSemiCircle; // Vertex buffer objects related fields std::deque vboItems; ///< Stores informations about VBO objects VBO_ITEM* curVboItem; ///< Currently used VBO_ITEM (for grouping) - GLuint curVboVertId; ///< Currently used vertices VBO handle - GLuint curVboIndId; ///< Currently used indices VBO handle - int vboSize; ///< Amount of vertices stored in VBO + VBO_CONTAINER* vboContainer; ///< Container for storing VBO_ITEMs + GLuint vboVertices; ///< Currently used vertices VBO handle + GLuint vboIndices; ///< Currently used indices VBO handle bool vboNeedsUpdate; ///< Flag indicating if VBO should be rebuilt glm::mat4 transform; ///< Current transformation matrix std::stack transformStack; ///< Stack of transformation matrices @@ -361,7 +372,8 @@ private: std::deque unitCirclePoints; ///< List of the points on a unit circle // Polygon tesselation - GLUtesselator* tesselator; ///< Pointer to the tesselator + GLUtesselator* tesselator; ///< Pointer to the tesselator + std::vector tessIntersects; ///< Storage of intersecting points // Shader // Possible types of shaders @@ -519,12 +531,20 @@ private: * @brief Starts drawing in immediate mode or does nothing if an item's caching has started. * @param aMode specifies the primitive or primitives that will be created. */ - inline void begin( GLenum aMode ); + inline void begin( GLenum aMode ) + { + if( !isGrouping ) + glBegin( aMode ); + } /** * @brief Ends drawing in immediate mode or does nothing if an item's caching has started. */ - inline void end(); + inline void end() + { + if( !isGrouping ) + glEnd(); + } /** * @brief Adds vertex to the current item or draws it in immediate mode. @@ -532,7 +552,19 @@ private: * @param aY is Y coordinate. * @param aZ is Z coordinate. */ - inline void vertex3( double aX, double aY, double aZ ); + inline void vertex3( double aX, double aY, double aZ ) + { + if( isGrouping ) + { + // New vertex coordinates for VBO + VBO_VERTEX vertex( aX, aY, aZ ); + curVboItem->PushVertex( &vertex ); + } + else + { + glVertex3d( aX, aY, aZ ); + } + } /** * @brief Function that replaces glTranslate and behaves according to isGrouping variable. @@ -543,7 +575,17 @@ private: * @param aY is translation in Y axis direction. * @param aZ is translation in Z axis direction. */ - inline void translate3( double aX, double aY, double aZ ); + inline void translate3( double aX, double aY, double aZ ) + { + if( isGrouping ) + { + transform = glm::translate( transform, glm::vec3( aX, aY, aZ ) ); + } + else + { + glTranslated( aX, aY, aZ ); + } + } /** * @brief Function that replaces glColor and behaves according to isGrouping variable. @@ -555,7 +597,17 @@ private: * @param aB is blue component. * @param aA is alpha component. */ - inline void color4( double aRed, double aGreen, double aBlue, double aAlpha ); + inline void color4( double aRed, double aGreen, double aBlue, double aAlpha ) + { + if( isGrouping ) + { + curVboItem->UseColor( COLOR4D( aRed, aGreen, aBlue, aAlpha ) ); + } + else + { + glColor4d( aRed, aGreen, aBlue, aAlpha ); + } + } /** * @brief Function that replaces glColor and behaves according to isGrouping variable. @@ -564,7 +616,17 @@ private: * * @param aColor is the new color. */ - inline void color4( const COLOR4D& aColor ); + inline void color4( const COLOR4D& aColor ) + { + if( isGrouping ) + { + curVboItem->UseColor( aColor ); + } + else + { + glColor4d( aColor.r, aColor.g, aColor.b, aColor.a); + } + } /** * @brief Function that sets shader and its parameters for the currently used VBO_ITEM. diff --git a/include/gal/opengl/vbo_container.h b/include/gal/opengl/vbo_container.h new file mode 100644 index 0000000000..f39c59f2e5 --- /dev/null +++ b/include/gal/opengl/vbo_container.h @@ -0,0 +1,256 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * 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 + */ + +/** + * @file vbo_container.h + * @brief Class to store VBO_ITEMs. + */ + +#ifndef VBO_CONTAINER_H_ +#define VBO_CONTAINER_H_ + +#include +#include +#include + +namespace KiGfx +{ +class VBO_ITEM; +typedef struct VBO_VERTEX VBO_VERTEX; + +class VBO_CONTAINER +{ +public: + VBO_CONTAINER( int aSize = 1048576 ); + ~VBO_CONTAINER(); + + ///< Maps size of free memory chunks to their offsets + typedef std::pair Chunk; + typedef std::multimap FreeChunkMap; + + ///< Maps size of reserved memory chunks to their owners (VBO_ITEMs) + typedef std::pair ReservedChunk; + typedef std::multimap ReservedChunkMap; + + /** + * Function StartItem() + * Starts an unknown sized item. After calling the function it is possible to add vertices + * using function Add(). + * @param aVboItem is the item that is going to store vertices in the container. + */ + void StartItem( VBO_ITEM* aVboItem ); + + /** + * Function EndItem() + * Marks current item as finished and returns unused memory to the pool. StartItem() function + * has to be called first. + */ + void EndItem(); + + /** + * Function Add() + * Stores given number of vertices in the container for the specific VBO_ITEM. + * @param aVboItem is the owner of the vertices. + * @param aVertex are vertices data to be stored. + * @param aSize is the number of vertices to be added. + */ + void Add( VBO_ITEM* aVboItem, const VBO_VERTEX* aVertex, unsigned int aSize = 1 ); + + /** + * Function Free() + * Frees the chunk reserved by the aVboItem. + * @param aVboItem is the owner of the chunk to be freed. + */ + inline void Free( VBO_ITEM* aVboItem ) + { + ReservedChunkMap::iterator it = m_reservedChunks.find( aVboItem ); + free( it ); + + // Dynamic memory freeing, there is no point in holding + // a large amount of memory when there is no use for it + if( m_freeSpace > ( m_currentSize / 2 ) ) + { + resizeContainer( m_currentSize / 2 ); + } + } + + /** + * Function GetAllVertices() + * Returns all vertices stored in the container. It is especially useful for transferring + * data to the GPU memory. + */ + VBO_VERTEX* GetAllVertices() const; + + /** + * Function GetVertices() + * Returns vertices stored by the specific item. + * @aVboItem is the specific item. + */ + VBO_VERTEX* GetVertices( const VBO_ITEM* aVboItem ) const; + + /** + * Function GetSize() + * Returns amount of vertices currently stored in the container. + */ + inline int GetSize() const + { + return m_currentSize; + } + +private: + ///< Stores size & offset of free chunks. + FreeChunkMap m_freeChunks; + ///< Stores owners (VBO_ITEM*) of reserved chunks and their size & offset. + ReservedChunkMap m_reservedChunks; + + /** + * Function allocate() + * Finds an offset where the number of vertices can be stored in a continous space. If there is + * no such chunk, appropriate amount of memory is allocated first. + * @param aVboItem is the owner of vertices to be stored. + * @param aSize is the number of vertices to be stored. + */ + unsigned int allocate( VBO_ITEM* aVboItem, unsigned int aSize ); + + /** + * Function getChunkSize() + * Returns size of the given chunk (works both for reserved and free chunks). + * @param aChunk is the chunk. + */ + inline int getChunkSize( const Chunk& aChunk ) const + { + return aChunk.first; + } + + inline int getChunkSize( const ReservedChunk& aChunk ) const + { + return aChunk.second.first; + } + + /** + * Function getChunkOffset() + * Returns offset of the given chunk (works both for reserved and free chunks). + * @param aChunk is the chunk. + */ + inline unsigned int getChunkOffset( const Chunk& aChunk ) const + { + return aChunk.second; + } + + inline unsigned int getChunkOffset( const ReservedChunk& aChunk ) const + { + return aChunk.second.second; + } + + /** + * Function getChunkOffset() + * Upadtes offset of the given chunk (works both for reserved and free chunks). + * !! IMPORTANT: it does not reallocate the chunk, it just changes its properties. + * @param aChunk is the chunk. + */ + inline void setChunkOffset( Chunk& aChunk, unsigned int aOffset ) const + { + aChunk.second = aOffset; + } + + inline void setChunkOffset( ReservedChunk& aChunk, unsigned int aOffset ) const + { + aChunk.second.second = aOffset; + } + + /** + * Function getChunkVboItem() + * Returns owner of the given reserved chunk. + * @param aChunk is the chunk. + */ + inline VBO_ITEM* getChunkVboItem( const ReservedChunk& aChunk ) const + { + return aChunk.first; + } + + /** + * Function defragment() + * Removes empty spaces between chunks, so after that there is a long continous space + * for storing vertices at the and of the container. + * @return false in case of failure (eg. memory shortage) + */ + bool defragment( VBO_VERTEX* aTarget = NULL ); + + /** + * Function resizeChunk() + * Changes size of the chunk that stores vertices of aVboItem. + * @param aVboItem is the item for which reserved space size should be changed. + * @param aNewSize is the new size for the aVboItem, expressed in vertices number. + */ + void resizeChunk( VBO_ITEM* aVboItem, int aNewSize ); + + /** + * Function resizeContainer() + * Prepares a bigger container of a given size. + * @param aNewSize is the new size of container, expressed in vertices + * @return false in case of failure (eg. memory shortage) + */ + bool resizeContainer( unsigned int aNewSize ); + + /** + * Function free() + * Frees the space described in aChunk and returns it to the free space pool. + * @param aChunk is a space to be freed. + */ + void free( const ReservedChunkMap::iterator& aChunk ); + + ///< How many vertices we can store in the container + unsigned int m_freeSpace; + + ///< How big is the current container, expressed in vertices + unsigned int m_currentSize; + + ///< Actual storage memory + VBO_VERTEX* m_vertices; + + ///< A flag saying if there is the item with an unknown size being added + bool itemStarted; + + ///< Variables holding the state of the item currently being added + unsigned int itemSize, itemChunkSize; + VBO_ITEM* item; + + /** + * 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 ) + { + unsigned int power = 1; + + while( power < aNumber && power ) + power <<= 1; + + return power; + } +}; +} // namespace KiGfx + +#endif /* VBO_CONTAINER_H_ */ diff --git a/include/gal/opengl/vbo_item.h b/include/gal/opengl/vbo_item.h index a3b476cbb5..8f1d875911 100644 --- a/include/gal/opengl/vbo_item.h +++ b/include/gal/opengl/vbo_item.h @@ -36,8 +36,6 @@ #include -#include - namespace KiGfx { typedef struct VBO_VERTEX @@ -45,12 +43,31 @@ typedef struct VBO_VERTEX GLfloat x, y, z; // Coordinates GLfloat r, g, b, a; // Color GLfloat shader[4]; // Shader type & params + GLfloat _padding; + + VBO_VERTEX() + {} + + VBO_VERTEX( const GLfloat aX, const GLfloat aY, const GLfloat aZ ) : + x( aX ), y( aY ), z( aZ ) + {} + + VBO_VERTEX( const GLfloat *aData ) : + x( aData[0] ), y( aData[1] ), z( aData[2] ) + {} + + operator GLfloat*() + { + return &x; + } } VBO_VERTEX; +class VBO_CONTAINER; + class VBO_ITEM { public: - VBO_ITEM(); + VBO_ITEM( VBO_CONTAINER* aContainer ); ~VBO_ITEM(); /** @@ -60,7 +77,7 @@ public: * @param aVertex is a vertex to be added. * @param aShader is an attribute for shader. */ - void PushVertex( const GLfloat* aVertex ); + void PushVertex( VBO_VERTEX* aVertex ); /** * Function PushVertices() @@ -71,14 +88,14 @@ public: * @param aSize is an amount of vertices to be added. * @param aShader is an attribute for shader. */ - void PushVertices( const GLfloat* aVertices, GLuint aSize ); + void PushVertices( VBO_VERTEX* aVertices, GLuint aSize ); /** * Function GetVertices() * Returns a pointer to the array containing all vertices. * @return Pointer to vertices packed in format {X, Y, Z, R, G, B, A}. */ - GLfloat* GetVertices(); + VBO_VERTEX* GetVertices(); /** @@ -86,7 +103,7 @@ public: * Returns information about number of vertices stored. * @param Amount of vertices. */ - inline int GetSize() const + inline unsigned int GetSize() const { return m_size; } @@ -96,7 +113,7 @@ public: * Sets data offset in the VBO. * @param aOffset is the offset expressed as a number of vertices. */ - void SetOffset( int aOffset ) + void SetOffset( unsigned int aOffset ) { m_offset = aOffset; } @@ -106,7 +123,7 @@ public: * Returns data offset in the VBO. * @return Data offset expressed as a number of vertices. */ - inline int GetOffset() const + inline unsigned int GetOffset() const { return m_offset; } @@ -156,16 +173,8 @@ public: } } - - inline void FreeVerticesData() - { - if( m_vertices && !m_isDirty ) - { - delete[] m_vertices; - m_vertices = NULL; - } - } - + ///< Informs the container that there will be no more vertices for the current VBO_ITEM + void Finish(); ///< Data organization information for vertices {X,Y,Z,R,G,B,A} (@see VBO_VERTEX). static const int VertByteSize = sizeof(VBO_VERTEX); @@ -191,40 +200,24 @@ public: static const int IndByteSize = sizeof(GLuint); private: - ///< Contains vertices coordinates and colors. - ///< Packed by 7 floats for each vertex: {X, Y, Z, R, G, B, A} - GLfloat* m_vertices; + ///< Offset and size of data stored in the VBO_CONTAINER. + unsigned int m_offset; + unsigned int m_size; - ///< Lists of data blocks storing vertices - std::list m_vertBlocks; - - ///< Pointers to current blocks that should be used for storing data - VBO_VERTEX* m_vertPtr; - - ///< How many vertices can be stored in the current buffer - int m_spaceLeft; - ///< Number of vertices stored in a single block - static const int BLOCK_SIZE = 256; - ///< Creates a new block for storing vertices data - void useNewBlock(); - ///< Prepares a continuous block of data that can be copied to graphics card buffer. - void prepareFinal(); - - ///< Offset and size of data in VBO. - int m_offset; - int m_size; + ///< Storage for vertices. + VBO_CONTAINER* m_container; ///< Color used for new vertices pushed. - GLfloat m_color[ColorStride]; + GLfloat m_color[ColorStride]; ///< Shader and its parameters used for new vertices pushed - GLfloat m_shader[ShaderStride]; + GLfloat m_shader[ShaderStride]; ///< Flag telling if the item should be recached in VBO or not. - bool m_isDirty; + bool m_isDirty; ///< Current transform matrix applied for every new vertex pushed. - const glm::mat4* m_transform; + const glm::mat4* m_transform; }; } // namespace KiGfx diff --git a/include/view/view_item.h b/include/view/view_item.h index 019b00fb72..55d7df7080 100644 --- a/include/view/view_item.h +++ b/include/view/view_item.h @@ -68,7 +68,7 @@ public: ALL = 0xff }; - VIEW_ITEM() : m_view( NULL ), m_viewVisible( true ), m_groupsSize( 0 ) {} + VIEW_ITEM() : m_view( NULL ), m_viewVisible( true ), m_groups( NULL ), m_groupsSize( 0 ) {} /** * Destructor. For dynamic views, removes the item from the view. @@ -76,6 +76,7 @@ public: virtual ~VIEW_ITEM() { ViewRelease(); + delete[] m_groups; }; /**