Optimizations/fixes to the VIEW/GAL classes:

- much faster Cairo rendering (outperforms legacy)
- improvements in VIEW update handling
- fixed issue with grid rendering in flip view mode
This commit is contained in:
Tomasz Włostowski 2016-12-07 10:20:31 +01:00 committed by Maciej Suminski
parent 3f7c5a0845
commit da28e163d2
14 changed files with 168 additions and 105 deletions

View File

@ -531,6 +531,7 @@ endif()
# Find Cairo library, required
#
find_package( Cairo 1.8.8 REQUIRED )
find_package( Pixman 1.0 REQUIRED )
#
# Find Boost library, required.

View File

@ -4,6 +4,7 @@ include_directories(
./widgets
./dialog_about
${CAIRO_INCLUDE_DIR}
${PIXMAN_INCLUDE_DIR}
${GLEW_INCLUDE_DIR}
${GLM_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}

View File

@ -89,7 +89,7 @@ unsigned int CAIRO_COMPOSITOR::CreateBuffer()
#endif /* __WXDEBUG__ */
// Set default settings for the buffer
cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL );
cairo_set_antialias( context, CAIRO_ANTIALIAS_NONE );
cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );

View File

@ -33,11 +33,11 @@
#include <limits>
#include <pixman.h>
using namespace KIGFX;
const float CAIRO_GAL::LAYER_ALPHA = 0.8;
CAIRO_GAL::CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
wxEvtHandler* aPaintListener, const wxString& aName ) :
@ -111,46 +111,94 @@ void CAIRO_GAL::BeginDrawing()
compositor->SetBuffer( mainBuffer );
// Cairo grouping prevents display of overlapping items on the same layer in the lighter color
cairo_push_group( currentContext );
//cairo_push_group( currentContext );
}
#include <profile.h>
void CAIRO_GAL::EndDrawing()
{
printf("EndDRAW!\n\n\n");
// Force remaining objects to be drawn
Flush();
// Cairo grouping prevents display of overlapping items on the same layer in the lighter color
cairo_pop_group_to_source( currentContext );
cairo_paint_with_alpha( currentContext, LAYER_ALPHA );
//cairo_pop_group_to_source( currentContext );
//cairo_paint_with_alpha( currentContext, LAYER_ALPHA );
// Merge buffers on the screen
PROF_COUNTER comp("cairo-comp");
compositor->DrawBuffer( mainBuffer );
compositor->DrawBuffer( overlayBuffer );
comp.show();
// This code was taken from the wxCairo example - it's not the most efficient one
// Here is a good place for optimizations
// Now translate the raw context data from the format stored
// by cairo into a format understood by wxImage.
PROF_COUNTER draw("cairo-draw");
unsigned char* wxOutputPtr = wxOutput;
for( size_t count = 0; count < bufferSize; count++ )
printf("W %d sw %d\n", wxBufferWidth, screenSize.x);
pixman_image_t *dstImg = pixman_image_create_bits (PIXMAN_r8g8b8, screenSize.x, screenSize.y, (uint32_t*)wxOutput, wxBufferWidth * 3);
pixman_image_t *srcImg = pixman_image_create_bits (PIXMAN_a8b8g8r8, screenSize.x, screenSize.y, (uint32_t*)bitmapBuffer, wxBufferWidth * 4);
pixman_image_composite (PIXMAN_OP_SRC, srcImg, NULL, dstImg,
0, 0, 0, 0, 0, 0, screenSize.x, screenSize.y );
pixman_image_unref (srcImg);
// free (srcImg);
pixman_image_unref (dstImg);
//free (dstImg);
/*for( size_t count = 0; count < bufferSize; count++ )
{
unsigned int value = bitmapBuffer[count];
*wxOutputPtr++ = ( value >> 16 ) & 0xff; // Red pixel
*wxOutputPtr++ = ( value >> 8 ) & 0xff; // Green pixel
*wxOutputPtr++ = value & 0xff; // Blue pixel
}
}*/
draw.show();
PROF_COUNTER wxd1("wx-draw");
wxImage img( wxBufferWidth, screenSize.y, (unsigned char*) wxOutput, true );
wxd1.show();
PROF_COUNTER wxd2("wx-draw2");
wxImage img( screenSize.x, screenSize.y, (unsigned char*) wxOutput, true );
wxBitmap bmp( img );
wxClientDC client_dc( this );
wxBufferedDC dc;
dc.Init( &client_dc, bmp );
wxd2.show();
PROF_COUNTER wxd3("wx-draw2");
wxMemoryDC mdc ( bmp );
wxd3.show();
PROF_COUNTER wxd("wx-drawf");
wxClientDC clientDC( this );
//wxBufferedDC dc;
//dc.Init( &client_dc, bmp );
wxd.show();
PROF_COUNTER wxb("wx-blt");
blitCursor( mdc );
clientDC.Blit( 0, 0, screenSize.x, screenSize.y, &mdc, 0, 0, wxCOPY );
wxb.show();
// Now it is the time to blit the mouse cursor
blitCursor( dc );
deinitSurface();
}
@ -158,8 +206,12 @@ void CAIRO_GAL::EndDrawing()
void CAIRO_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
{
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
flushPath();
// cairo_set_source_rgb( currentContext, gridColor.r, gridColor.g, gridColor.b );
//cairo_stroke( currentContext );
isElementAdded = true;
}
@ -174,6 +226,8 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo
cairo_move_to( currentContext, (double) aStartPoint.x, (double) aStartPoint.y );
cairo_line_to( currentContext, (double) aEndPoint.x, (double) aEndPoint.y );
cairo_set_source_rgba( currentContext, fillColor.r, fillColor.g, fillColor.b, fillColor.a);
cairo_stroke( currentContext );
}
else
{
@ -197,6 +251,7 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo
cairo_line_to( currentContext, lineLength, -aWidth / 2.0 );
cairo_restore( currentContext );
flushPath();
}
isElementAdded = true;
@ -205,10 +260,9 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo
void CAIRO_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
{
// A circle is drawn using an arc
cairo_new_sub_path( currentContext );
cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI );
flushPath();
isElementAdded = true;
}
@ -233,6 +287,7 @@ void CAIRO_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aS
cairo_line_to( currentContext, endPoint.x, endPoint.y );
cairo_close_path( currentContext );
}
flushPath();
isElementAdded = true;
}
@ -250,6 +305,7 @@ void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEnd
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
cairo_line_to( currentContext, diagonalPointB.x, diagonalPointB.y );
cairo_close_path( currentContext );
flushPath();
isElementAdded = true;
}
@ -263,6 +319,7 @@ void CAIRO_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControl
aControlPointB.y, aEndPoint.x, aEndPoint.y );
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
flushPath();
isElementAdded = true;
}
@ -408,10 +465,10 @@ void CAIRO_GAL::SetLayerDepth( double aLayerDepth )
{
storePath();
cairo_pop_group_to_source( currentContext );
cairo_paint_with_alpha( currentContext, LAYER_ALPHA );
//cairo_pop_group_to_source( currentContext );
//cairo_paint_with_alpha( currentContext, LAYER_ALPHA );
cairo_push_group( currentContext );
//cairo_push_group( currentContext );
}
}
@ -593,7 +650,7 @@ void CAIRO_GAL::DrawGroup( int aGroupNumber )
case CMD_STROKE_PATH:
cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b );
cairo_append_path( currentContext, it->cairoPath );
cairo_stroke( currentContext );
cairo_stroke( currentContext );
break;
case CMD_FILL_PATH:
@ -734,8 +791,8 @@ void CAIRO_GAL::SetTarget( RENDER_TARGET aTarget )
{
storePath();
cairo_pop_group_to_source( currentContext );
cairo_paint_with_alpha( currentContext, LAYER_ALPHA );
//cairo_pop_group_to_source( currentContext );
//cairo_paint_with_alpha( currentContext, LAYER_ALPHA );
}
switch( aTarget )
@ -751,8 +808,8 @@ void CAIRO_GAL::SetTarget( RENDER_TARGET aTarget )
break;
}
if( isInitialized )
cairo_push_group( currentContext );
//if( isInitialized )
//cairo_push_group( currentContext );
currentTarget = aTarget;
}
@ -799,21 +856,36 @@ void CAIRO_GAL::SetCursorSize( unsigned int aCursorSize )
void CAIRO_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
{
// Now we should only store the position of the mouse cursor
// The real drawing routines are in blitCursor()
cursorPosition = VECTOR2D( aCursorPosition.x - cursorSize / 2,
aCursorPosition.y - cursorSize / 2 );
cursorPosition = aCursorPosition;
}
void CAIRO_GAL::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
{
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
cairo_set_source_rgb( currentContext, gridColor.r, gridColor.g, gridColor.b );
cairo_set_source_rgba( currentContext, gridColor.r, gridColor.g, gridColor.b, strokeColor.a );
cairo_stroke( currentContext );
}
void CAIRO_GAL::flushPath()
{
if( isFillEnabled )
{
cairo_set_source_rgba( currentContext, fillColor.r, fillColor.g, fillColor.b, fillColor.a );
if( isStrokeEnabled )
cairo_fill_preserve( currentContext );
else
cairo_fill( currentContext );
}
if( isStrokeEnabled )
{
cairo_set_source_rgba( currentContext, strokeColor.r, strokeColor.g,
strokeColor.b, strokeColor.a );
cairo_stroke( currentContext );
}
}
void CAIRO_GAL::storePath()
{
@ -902,47 +974,32 @@ void CAIRO_GAL::initCursor()
}
void CAIRO_GAL::blitCursor( wxBufferedDC& clientDC )
void CAIRO_GAL::blitCursor( wxMemoryDC& clientDC )
{
if( !isCursorEnabled )
return;
wxMemoryDC cursorSave( *cursorPixelsSaved );
wxMemoryDC cursorShape( *cursorPixels );
auto p = ToScreen( cursorPosition );
if( !isDeleteSavedPixels )
{
// Restore pixels that were overpainted by the previous cursor
clientDC.Blit( savedCursorPosition.x, savedCursorPosition.y,
cursorSize, cursorSize, &cursorSave, 0, 0 );
}
else
{
isDeleteSavedPixels = false;
}
clientDC.SetPen( *wxWHITE_PEN );
clientDC.DrawLine ( p.x - cursorSize / 2, p.y, p.x + cursorSize / 2, p.y );
clientDC.DrawLine ( p.x, p.y - cursorSize / 2, p.x, p.y + cursorSize / 2 );
// Store pixels that are going to be overpainted
VECTOR2D cursorScreen = ToScreen( cursorPosition ) - cursorSize / 2.0f;
cursorSave.Blit( 0, 0, cursorSize, cursorSize, &clientDC, cursorScreen.x, cursorScreen.y );
// Draw the cursor
clientDC.Blit( cursorScreen.x, cursorScreen.y, cursorSize, cursorSize,
&cursorShape, 0, 0, wxOR );
savedCursorPosition.x = (wxCoord) cursorScreen.x;
savedCursorPosition.y = (wxCoord) cursorScreen.y;
}
void CAIRO_GAL::allocateBitmaps()
{
wxBufferWidth = screenSize.x;
while( ((wxBufferWidth * 3) % 4) != 0 ) wxBufferWidth++;
// Create buffer, use the system independent Cairo context backend
stride = cairo_format_stride_for_width( GAL_FORMAT, screenSize.x );
stride = cairo_format_stride_for_width( GAL_FORMAT, wxBufferWidth );
bufferSize = stride * screenSize.y;
bitmapBuffer = new unsigned int[bufferSize];
bitmapBufferBackup = new unsigned int[bufferSize];
wxOutput = new unsigned char[bufferSize * 3];
wxOutput = new unsigned char[wxBufferWidth * 3 * screenSize.y];
}
@ -961,7 +1018,7 @@ void CAIRO_GAL::initSurface()
// Create the Cairo surface
surface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer, GAL_FORMAT,
screenSize.x, screenSize.y, stride );
wxBufferWidth, screenSize.y, stride );
context = cairo_create( surface );
#ifdef __WXDEBUG__
cairo_status_t status = cairo_status( context );
@ -969,7 +1026,7 @@ void CAIRO_GAL::initSurface()
#endif /* __WXDEBUG__ */
currentContext = context;
cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL );
cairo_set_antialias( context, CAIRO_ANTIALIAS_NONE );
// Clear the screen
ClearScreen( backgroundColor );
@ -1037,6 +1094,9 @@ void CAIRO_GAL::drawPoly( const std::deque<VECTOR2D>& aPointList )
cairo_line_to( currentContext, it->x, it->y );
}
flushPath();
//cairo_fill( currentContext );
isElementAdded = true;
}
@ -1054,6 +1114,8 @@ void CAIRO_GAL::drawPoly( const VECTOR2D aPointList[], int aListSize )
cairo_line_to( currentContext, ptr->x, ptr->y );
}
flushPath();
isElementAdded = true;
}

View File

@ -151,8 +151,6 @@ void GAL::DrawGrid()
int gridEndX = KiROUND( worldEndPoint.x / gridSize.x );
int gridStartY = KiROUND( worldStartPoint.y / gridSize.y );
int gridEndY = KiROUND( worldEndPoint.y / gridSize.y );
int dirX = gridEndX >= gridStartX ? 1 : -1;
int dirY = gridEndY >= gridStartY ? 1 : -1;
// Correct the index, else some lines are not correctly painted
gridStartX -= std::abs( gridOrigin.x / gridSize.x ) + 1;
@ -160,6 +158,9 @@ void GAL::DrawGrid()
gridEndX += std::abs( gridOrigin.x / gridSize.x ) + 1;
gridEndY += std::abs( gridOrigin.y / gridSize.y ) + 1;
int dirX = gridEndX >= gridStartX ? 1 : -1;
int dirY = gridEndY >= gridStartY ? 1 : -1;
// Draw the grid behind all other layers
SetLayerDepth( depthRange.y * 0.75 );

View File

@ -878,8 +878,6 @@ void OPENGL_GAL::DrawGrid()
int gridEndX = KiROUND( worldEndPoint.x / gridSize.x );
int gridStartY = KiROUND( worldStartPoint.y / gridSize.y );
int gridEndY = KiROUND( worldEndPoint.y / gridSize.y );
int dirX = gridStartX >= gridEndX ? -1 : 1;
int dirY = gridStartY >= gridEndY ? -1 : 1;
// Correct the index, else some lines are not correctly painted
gridStartX -= std::abs( gridOrigin.x / gridSize.x ) + 1;
@ -887,6 +885,9 @@ void OPENGL_GAL::DrawGrid()
gridEndX += std::abs( gridOrigin.x / gridSize.x ) + 1;
gridEndY += std::abs( gridOrigin.y / gridSize.y ) + 1;
int dirX = gridStartX >= gridEndX ? -1 : 1;
int dirY = gridStartY >= gridEndY ? -1 : 1;
glDisable( GL_DEPTH_TEST );
glDisable( GL_TEXTURE_2D );

View File

@ -262,7 +262,7 @@ VIEW::VIEW( bool aIsDynamic ) :
m_dynamic( aIsDynamic )
{
m_boundary.SetMaximum();
m_needsUpdate.reserve( 32768 );
m_allItems.reserve( 32768 );
// Redraw everything at the beginning
MarkDirty();
@ -310,6 +310,8 @@ void VIEW::Add( VIEW_ITEM* aItem )
aItem->ViewGetLayers( layers, layers_count );
aItem->viewPrivData()->saveLayers( layers, layers_count );
m_allItems.push_back(aItem);
for( int i = 0; i < layers_count; ++i )
{
VIEW_LAYER& l = m_layers[layers[i]];
@ -326,21 +328,18 @@ void VIEW::Remove( VIEW_ITEM* aItem )
{
if ( !aItem )
return;
auto viewData = aItem->viewPrivData();
if ( !viewData )
return;
if( viewData->requiredUpdate() != NONE ) // prevent from updating a removed item
{
std::vector<VIEW_ITEM*>::iterator item = std::find( m_needsUpdate.begin(),
m_needsUpdate.end(), aItem );
auto item = std::find( m_allItems.begin(), m_allItems.end(), aItem );
if( item != m_needsUpdate.end() )
{
m_needsUpdate.erase( item );
viewData->clearUpdateFlags();
}
if( item != m_allItems.end() )
{
m_allItems.erase( item );
viewData->clearUpdateFlags();
}
int layers[VIEW::VIEW_MAX_LAYERS], layers_count;
@ -360,6 +359,7 @@ void VIEW::Remove( VIEW_ITEM* aItem )
}
viewData->deleteGroups();
aItem->m_viewPrivData = nullptr;
}
@ -946,10 +946,7 @@ void VIEW::Clear()
r.SetMaximum();
for( VIEW_ITEM* item : m_needsUpdate )
item->viewPrivData()->clearUpdateFlags();
m_needsUpdate.clear();
m_allItems.clear();
for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i )
{
@ -1237,16 +1234,16 @@ void VIEW::UpdateItems()
{
m_gal->BeginUpdate();
for( VIEW_ITEM* item : m_needsUpdate )
for( VIEW_ITEM* item : m_allItems )
{
auto viewData = item->viewPrivData();
assert( viewData->m_requiredUpdate != NONE );
invalidateItem( item, viewData->m_requiredUpdate );
if ( viewData->m_requiredUpdate != NONE )
invalidateItem( item, viewData->m_requiredUpdate );
viewData->m_requiredUpdate = NONE;
}
m_gal->EndUpdate();
m_needsUpdate.clear();
}
@ -1341,31 +1338,10 @@ void VIEW::Update( VIEW_ITEM *aItem, int aUpdateFlags )
assert( aUpdateFlags != NONE );
bool firstTime = (viewData->m_requiredUpdate == NONE);
viewData->m_requiredUpdate |= aUpdateFlags;
if( firstTime )
{
MarkForUpdate( aItem );
}
}
void VIEW::MarkForUpdate( VIEW_ITEM* aItem )
{
auto viewData = aItem->viewPrivData();
assert( viewData->m_requiredUpdate != NONE );
for ( auto item : m_needsUpdate )
assert(item != aItem);
m_needsUpdate.push_back( aItem );
}
const int VIEW::TOP_LAYER_MODIFIER = -VIEW_MAX_LAYERS;
};

View File

@ -118,7 +118,7 @@ void VIEW_GROUP::ViewDraw( int aLayer, VIEW* aView ) const
for( int i = 0; i < layers_count; i++ )
{
if( aView->IsCached( layers[i] ) && aView->IsLayerVisible( layers[i] ) )
if( aView->IsLayerVisible( layers[i] ) )
{
gal->AdvanceDepth();

View File

@ -72,7 +72,7 @@ public:
* Switches method of rendering graphics.
* @param aGalType is a type of rendering engine that you want to use.
*/
bool SwitchBackend( GAL_TYPE aGalType );
virtual bool SwitchBackend( GAL_TYPE aGalType );
/**
* Function GetBackend

View File

@ -346,6 +346,9 @@ private:
bool isInitialized; ///< Are Cairo image & surface ready to use
COLOR4D backgroundColor; ///< Background color
int wxBufferWidth;
void flushPath();
// Methods
void storePath(); ///< Store the actual path
@ -372,7 +375,7 @@ private:
/**
* @brief Blits cursor into the current screen.
*/
virtual void blitCursor( wxBufferedDC& clientDC );
virtual void blitCursor( wxMemoryDC& clientDC );
/// Prepare Cairo surfaces for drawing
void initSurface();

View File

@ -119,7 +119,9 @@ public:
{
stop();
fprintf(stderr,"%s took %.1f milliseconds.\n", m_name.c_str(), (double)m_cnt.msecs());
start();
}
double msecs() const {
return m_cnt.msecs();
}

View File

@ -751,8 +751,8 @@ private:
/// Rendering order modifier for layers that are marked as top layers
static const int TOP_LAYER_MODIFIER;
/// Items to be updated
std::vector<VIEW_ITEM*> m_needsUpdate;
/// Flat list of all items
std::vector<VIEW_ITEM*> m_allItems;
};
} // namespace KIGFX

View File

@ -379,6 +379,12 @@ void PCB_DRAW_PANEL_GAL::setDefaultLayerOrder()
}
}
bool PCB_DRAW_PANEL_GAL::SwitchBackend( GAL_TYPE aGalType )
{
bool rv = EDA_DRAW_PANEL_GAL::SwitchBackend ( aGalType );
setDefaultLayerDeps();
return rv;
}
void PCB_DRAW_PANEL_GAL::setDefaultLayerDeps()
{
@ -400,6 +406,13 @@ void PCB_DRAW_PANEL_GAL::setDefaultLayerDeps()
}
}
// caching makes no sense for Cairo and other software renderers
if ( m_backend != GAL_TYPE_OPENGL )
{
for (int i = 0; i < KIGFX::VIEW::VIEW_MAX_LAYERS; i++)
m_view->SetLayerTarget(i, KIGFX::TARGET_NONCACHED);
}
m_view->SetLayerTarget( ITEM_GAL_LAYER( ANCHOR_VISIBLE ), KIGFX::TARGET_NONCACHED );
m_view->SetLayerDisplayOnly( ITEM_GAL_LAYER( ANCHOR_VISIBLE ) );
@ -438,4 +451,5 @@ void PCB_DRAW_PANEL_GAL::setDefaultLayerDeps()
m_view->SetLayerDisplayOnly( ITEM_GAL_LAYER( WORKSHEET ) );
m_view->SetLayerDisplayOnly( ITEM_GAL_LAYER( GRID_VISIBLE ) );
m_view->SetLayerDisplayOnly( ITEM_GAL_LAYER( DRC_VISIBLE ) );
}

View File

@ -83,6 +83,8 @@ public:
///> @copydoc EDA_DRAW_PANEL_GAL::OnShow()
void OnShow() override;
bool SwitchBackend( GAL_TYPE aGalType ) override;
protected:
///> Reassigns layer order to the initial settings.
void setDefaultLayerOrder();