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.
This commit is contained in:
Maciej Suminski 2013-06-18 16:20:29 +02:00
parent 876bf75d89
commit cd517f67db
15 changed files with 925 additions and 336 deletions

View File

@ -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
)

View File

@ -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<GroupElement>::iterator it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end();
it != end; ++it )
std::deque<GroupElement>::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 )
{

View File

@ -29,8 +29,8 @@
#include <cmath>
#include <gal/opengl/opengl_gal.h>
#include <gal/opengl/vbo_container.h>
#include <gal/definitions.h>
#include <gal/opengl/glm/gtc/matrix_transform.hpp>
#include <wx/log.h>
#include <macros.h>
@ -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<VBO_ITEM*>::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<GLuint*>( 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<VBO_ITEM*>::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<VECTOR2D>& aPointList )
setShader( SHADER_NONE );
GLUtesselator* tesselator = gluNewTess();
typedef std::vector<OGLPOINT> 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, &params );
gluTessBeginContour( tesselator );
// use operator=( const POINTS& )
@ -1456,7 +1391,13 @@ void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
gluTessEndContour( tesselator );
gluTessEndPolygon( tesselator );
gluDeleteTess( tesselator );
// Free allocated intersecting points
std::vector<GLdouble*>::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<VBO_ITEM*>::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<VBO_ITEM*>::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<GLdouble*>( aVertexPtr );
OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
VBO_ITEM* vboItem = param->vboItem;
if( aData )
if( vboItem )
{
VBO_ITEM* vboItem = static_cast<VBO_ITEM*>( 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<OPENGL_GAL::TessParams*>( 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 );

View File

@ -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 <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
*/
/**
* @file vbo_container.cpp
* @brief Class to store VBO_ITEMs.
*/
#include <gal/opengl/vbo_container.h>
#include <gal/opengl/vbo_item.h>
#include <cstring>
#include <wx/log.h>
#ifdef __WXDEBUG__
#include <profile.h>
#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;
}

View File

@ -27,107 +27,98 @@
* @brief Class to handle an item held in a Vertex Buffer Object.
*/
#include <gal/opengl/vbo_container.h>
#include <gal/opengl/vbo_item.h>
#include <cstring>
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<VBO_VERTEX*>::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<VBO_VERTEX*>::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;
}

View File

@ -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++ )
{

View File

@ -79,6 +79,7 @@ PAINTER::PAINTER( GAL* aGal ) :
PAINTER::~PAINTER()
{
delete m_stroke_font;
delete m_settings;
}

View File

@ -473,6 +473,11 @@ void VIEW::Clear()
l->items->RemoveAll();
}
if( m_useGroups )
{
m_gal->ClearCache();
}
}

View File

@ -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;
}
}

View File

@ -212,6 +212,9 @@ public:
/// @copydoc GAL::DeleteGroup()
virtual void DeleteGroup( int aGroupNumber );
/// @copydoc GAL::ClearCache()
virtual void ClearCache();
// --------------------------------------------------------
// Handling the world <-> screen transformation
// --------------------------------------------------------

View File

@ -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
// --------------------------------------------------------

View File

@ -35,6 +35,8 @@
// OpenGL mathematics library
#define GLM_FORCE_RADIANS
#include <gal/opengl/glm/gtc/matrix_transform.hpp>
#include <gal/opengl/vbo_item.h>
#include <gal/opengl/shader.h>
@ -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<GLdouble*>& 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<VBO_ITEM*> 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<glm::mat4> transformStack; ///< Stack of transformation matrices
@ -361,7 +372,8 @@ private:
std::deque<VECTOR2D> 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<GLdouble*> 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.

View File

@ -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 <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
*/
/**
* @file vbo_container.h
* @brief Class to store VBO_ITEMs.
*/
#ifndef VBO_CONTAINER_H_
#define VBO_CONTAINER_H_
#include <GL/gl.h>
#include <map>
#include <wx/log.h>
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<const unsigned int, unsigned int> Chunk;
typedef std::multimap<const unsigned int, unsigned int> FreeChunkMap;
///< Maps size of reserved memory chunks to their owners (VBO_ITEMs)
typedef std::pair<VBO_ITEM* const, Chunk> ReservedChunk;
typedef std::multimap<VBO_ITEM* const, Chunk> 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_ */

View File

@ -36,8 +36,6 @@
#include <cstddef>
#include <list>
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<VBO_VERTEX*> 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

View File

@ -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;
};
/**