Performance Improvement: Use texture caching with xAxis tickmarks and text when usePixmapCache is on

This commit is contained in:
Mark Watkins 2013-01-18 17:33:35 +10:00
parent 866b9151ab
commit e24cc56975
6 changed files with 219 additions and 167 deletions

View File

@ -1080,10 +1080,8 @@ gGraph::gGraph(gGraphView *graphview,QString title,QString units, int height,sho
m_selecting_area=m_blockzoom=false; m_selecting_area=m_blockzoom=false;
m_lastx23=0; m_lastx23=0;
titleImage=QImage();
yAxisImage=QImage();
yAxisImageTex=titleImageTex=0;
invalidate_yAxisImage=true; invalidate_yAxisImage=true;
invalidate_xAxisImage=true;
m_quad=new gVertexBuffer(64,GL_QUADS); m_quad=new gVertexBuffer(64,GL_QUADS);
m_quad->forceAntiAlias(true); m_quad->forceAntiAlias(true);
@ -1994,6 +1992,7 @@ QPixmap gGraph::renderPixmap(int w, int h, bool printing)
// Sets a new Min & Max X clipping, refreshing the graph and all it's layers. // Sets a new Min & Max X clipping, refreshing the graph and all it's layers.
void gGraph::SetXBounds(qint64 minx, qint64 maxx) void gGraph::SetXBounds(qint64 minx, qint64 maxx)
{ {
invalidate_xAxisImage=true;
min_x=minx; min_x=minx;
max_x=maxx; max_x=maxx;
@ -2007,6 +2006,7 @@ int gGraph::flipY(int y)
void gGraph::ResetBounds() void gGraph::ResetBounds()
{ {
invalidate_xAxisImage=true;
min_x=MinX(); min_x=MinX();
max_x=MaxX(); max_x=MaxX();
min_y=MinY(); min_y=MinY();

View File

@ -730,12 +730,10 @@ public:
Layer * getLineChart(); Layer * getLineChart();
QRect m_lastbounds; QRect m_lastbounds;
QTimer * timer; QTimer * timer;
QImage titleImage,yAxisImage;
GLuint titleImageTex,yAxisImageTex;
// This gets set to true to force a redraw of the yAxis tickers when graphs are resized. // This gets set to true to force a redraw of the yAxis tickers when graphs are resized.
bool invalidate_yAxisImage; bool invalidate_yAxisImage;
bool invalidate_xAxisImage;
//! \brief Returns a Vector reference containing all this graphs layers //! \brief Returns a Vector reference containing all this graphs layers
QVector<Layer *> & layers() { return m_layers; } QVector<Layer *> & layers() { return m_layers; }

View File

@ -29,6 +29,7 @@ gXAxis::gXAxis(QColor col,bool fadeout)
m_show_major_ticks=true; m_show_major_ticks=true;
m_utcfix=false; m_utcfix=false;
m_fadeout=fadeout; m_fadeout=fadeout;
m_textureID=0;
// QDateTime d=QDateTime::currentDateTime(); // QDateTime d=QDateTime::currentDateTime();
// QTime t1=d.time(); // QTime t1=d.time();
// QTime t2=d.toUTC().time(); // QTime t2=d.toUTC().time();
@ -46,172 +47,218 @@ gXAxis::~gXAxis()
void gXAxis::paint(gGraph & w,int left,int top, int width, int height) void gXAxis::paint(gGraph & w,int left,int top, int width, int height)
{ {
Q_UNUSED(height) Q_UNUSED(height)
double px,py;
int start_px=left; QPainter painter; // Only need this for pixmap caching
//int start_py=top;
//int width=scrx-(w.GetLeftMargin()+w.GetRightMargin());
// float height=scry-(w.GetTopMargin()+w.GetBottomMargin());
if (width<40) bool usepixmap=w.graphView()->usePixmapCache(); // Whether or not to use pixmap caching
return;
qint64 minx;
qint64 maxx;
if (w.blockZoom()) {
minx=w.rmin_x;
maxx=w.rmax_x;
} else {
minx=w.min_x;
maxx=w.max_x;
}
qint64 xx=maxx-minx;
if (xx<=0) return;
//Most of this could be precalculated when min/max is set.. if (!usepixmap || (usepixmap && w.invalidate_xAxisImage)) {
QString fd,tmpstr;
int divmax,dividx;
int fitmode;
if (xx>=86400000L) { // Day
fd="Mjj 00";
dividx=0;
divmax=10;
fitmode=0;
} else if (xx>600000) { // Minutes
fd=" j0:00";
dividx=10;
divmax=27;
fitmode=1;
} else if (xx>5000) { // Seconds
fd=" j0:00:00";
dividx=16;
divmax=27;
fitmode=2;
} else { // Microseconds
fd="j0:00:00:000";
dividx=28;
divmax=divcnt;
fitmode=3;
}
//if (divmax>divcnt) divmax=divcnt;
int x,y; if (usepixmap) {
GetTextExtent(fd,x,y); // Unbind any previous texture
if (m_textureID) w.graphView()->deleteTexture(m_textureID);
if (x<=0) { m_pixmap=QPixmap(width+22,height+4);
qWarning() << "gXAxis::Plot() x<=0 font size bug"; m_pixmap.fill(Qt::transparent);
return; painter.begin(&m_pixmap);
} painter.setPen(Qt::black);
int max_ticks=width/(x+15); // Max number of ticks that will fit
int fit_ticks=0;
int div=-1;
qint64 closest=0,tmp,tmpft;
for (int i=dividx;i<divmax;i++){
tmpft=xx/divisors[i];
tmp=max_ticks-tmpft;
if (tmp<0) continue;
if (tmpft>closest) { // Find the closest scale to the number
closest=tmpft; // that will fit
div=i;
fit_ticks=tmpft;
} }
} double px,py;
if (fit_ticks==0) {
qDebug() << "gXAxis::Plot() Couldn't fit ticks.. Too short?" << minx << maxx << xx;
return;
}
if ((div<0) || (div>divcnt)) {
qDebug() << "gXAxis::Plot() div out of bounds";
return;
}
qint64 step=divisors[div];
//Align left minimum to divisor by losing precision int start_px=left;
qint64 aligned_start=minx/step; //int start_py=top;
aligned_start*=step; //int width=scrx-(w.GetLeftMargin()+w.GetRightMargin());
// float height=scry-(w.GetTopMargin()+w.GetBottomMargin());
while (aligned_start<minx) { if (width<40)
aligned_start+=step; return;
} qint64 minx;
qint64 maxx;
if (w.blockZoom()) {
minx=w.rmin_x;
maxx=w.rmax_x;
} else {
minx=w.min_x;
maxx=w.max_x;
}
qint64 xx=maxx-minx;
if (xx<=0) return;
gVertexBuffer *lines=w.backlines(); //Most of this could be precalculated when min/max is set..
lines->setColor(Qt::black); QString fd,tmpstr;
int divmax,dividx;
int fitmode;
if (xx>=86400000L) { // Day
fd="Mjj 00";
dividx=0;
divmax=10;
fitmode=0;
} else if (xx>600000) { // Minutes
fd=" j0:00";
dividx=10;
divmax=27;
fitmode=1;
} else if (xx>5000) { // Seconds
fd=" j0:00:00";
dividx=16;
divmax=27;
fitmode=2;
} else { // Microseconds
fd="j0:00:00:000";
dividx=28;
divmax=divcnt;
fitmode=3;
}
//if (divmax>divcnt) divmax=divcnt;
int x,y;
GetTextExtent(fd,x,y);
//int utcoff=m_utcfix ? tz_hours : 0; if (x<=0) {
qWarning() << "gXAxis::Plot() x<=0 font size bug";
//utcoff=0; return;
int num_minor_ticks;
if (step>=86400000) {
qint64 i=step/86400000L; // number of days
if (i>14) i/=2;
if (i<0) i=1;
num_minor_ticks=i;
} else num_minor_ticks=10;
float xmult=double(width)/double(xx);
float step_pixels=double(step/float(num_minor_ticks))*xmult;
py=left+float(aligned_start-minx)*xmult;
int mintop=top+4.0*(float(y)/10.0);
int majtop=top+6.0*(float(y)/10.0);
int texttop=majtop+y; // 18*w.printScaleY();
for (int i=0;i<num_minor_ticks;i++) {
py-=step_pixels;
if (py<start_px) continue;
lines->add(py,top,py,mintop);
}
//static QString dow[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
int ms,m,h,s,d;
qint64 j;
for (qint64 i=aligned_start;i<maxx;i+=step) {
px=(i-minx)*xmult;
px+=left;
lines->add(px,top,px,majtop);
j=i;
if (!m_utcfix) j+=tz_offset;
ms=j % 1000;
m=(j/60000L) % 60L;
h=(j/3600000L) % 24L;
s=(j/1000L) % 60L;
//int d=(j/86400000) % 7;
if (fitmode==0) {
d=(j/1000);
QDateTime dt=QDateTime::fromTime_t(d).toUTC();
tmpstr=dt.toString("MMM dd");
//} else if (fitmode==0) {
// tmpstr=QString("%1 %2:%3").arg(dow[d]).arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0'));
} else if (fitmode==1) { // minute
tmpstr=QString("%1:%2").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0'));
} else if (fitmode==2) { // second
tmpstr=QString("%1:%2:%3").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0')).arg(s,2,10,QChar('0'));
} else if (fitmode==3) { // milli
tmpstr=QString("%1:%2:%3:%4").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0')).arg(s,2,10,QChar('0')).arg(ms,3,10,QChar('0'));
} }
int tx=px-x/2.0; int max_ticks=width/(x+15); // Max number of ticks that will fit
if (m_utcfix) int fit_ticks=0;
tx+=step_pixels/2.0; int div=-1;
if ((tx+x)<(left+width)) qint64 closest=0,tmp,tmpft;
w.renderText(tmpstr,tx,texttop); for (int i=dividx;i<divmax;i++){
py=px; tmpft=xx/divisors[i];
for (int j=1;j<num_minor_ticks;j++) { tmp=max_ticks-tmpft;
py+=step_pixels; if (tmp<0) continue;
if (py>=left+width) break; if (tmpft>closest) { // Find the closest scale to the number
lines->add(py,top,py,mintop); closest=tmpft; // that will fit
div=i;
fit_ticks=tmpft;
}
}
if (fit_ticks==0) {
qDebug() << "gXAxis::Plot() Couldn't fit ticks.. Too short?" << minx << maxx << xx;
return;
}
if ((div<0) || (div>divcnt)) {
qDebug() << "gXAxis::Plot() div out of bounds";
return;
}
qint64 step=divisors[div];
//Align left minimum to divisor by losing precision
qint64 aligned_start=minx/step;
aligned_start*=step;
while (aligned_start<minx) {
aligned_start+=step;
} }
if (lines->full()) { gVertexBuffer *lines=w.backlines();
qWarning() << "maxverts exceeded in gXAxis::Plot()"; lines->setColor(Qt::black);
break;
//int utcoff=m_utcfix ? tz_hours : 0;
//utcoff=0;
int num_minor_ticks;
if (step>=86400000) {
qint64 i=step/86400000L; // number of days
if (i>14) i/=2;
if (i<0) i=1;
num_minor_ticks=i;
} else num_minor_ticks=10;
float xmult=double(width)/double(xx);
float step_pixels=double(step/float(num_minor_ticks))*xmult;
py=left+float(aligned_start-minx)*xmult;
//py+=usepixmap ? 20 : left;
int mintop=top+4.0*(float(y)/10.0);
int majtop=top+6.0*(float(y)/10.0);
int texttop=majtop+y; // 18*w.printScaleY();
// Fill in the minor tick marks up to the first major alignment tick
for (int i=0;i<num_minor_ticks;i++) {
py-=step_pixels;
if (py<start_px) continue;
if (usepixmap)
painter.drawLine(py-left+20,0,py-left+20,mintop-top);
else
lines->add(py,top,py,mintop);
} }
//static QString dow[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
int ms,m,h,s,d;
qint64 j;
for (qint64 i=aligned_start;i<maxx;i+=step) {
px=(i-minx)*xmult;
px+=left;
if (usepixmap) {
painter.drawLine(px-left+20,0,px-left+20,majtop-top);
} else lines->add(px,top,px,majtop);
j=i;
if (!m_utcfix) j+=tz_offset;
ms=j % 1000;
m=(j/60000L) % 60L;
h=(j/3600000L) % 24L;
s=(j/1000L) % 60L;
//int d=(j/86400000) % 7;
if (fitmode==0) {
d=(j/1000);
QDateTime dt=QDateTime::fromTime_t(d).toUTC();
tmpstr=dt.toString("MMM dd");
//} else if (fitmode==0) {
// tmpstr=QString("%1 %2:%3").arg(dow[d]).arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0'));
} else if (fitmode==1) { // minute
tmpstr=QString("%1:%2").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0'));
} else if (fitmode==2) { // second
tmpstr=QString("%1:%2:%3").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0')).arg(s,2,10,QChar('0'));
} else if (fitmode==3) { // milli
tmpstr=QString("%1:%2:%3:%4").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0')).arg(s,2,10,QChar('0')).arg(ms,3,10,QChar('0'));
}
int tx=px-x/2.0;
if (m_utcfix)
tx+=step_pixels/2.0;
if ((tx+x)<(left+width)) {
if (!usepixmap) w.renderText(tmpstr,tx,texttop);
else painter.drawText(tx-left+20,texttop-top,tmpstr);
}
py=px;
for (int j=1;j<num_minor_ticks;j++) {
py+=step_pixels;
if (py>=left+width) break;
if (usepixmap) {
painter.drawLine(py-left+20,0,py-left+20,mintop-top);
} else lines->add(py,top,py,mintop);
}
if (lines->full()) {
qWarning() << "maxverts exceeded in gXAxis::Plot()";
break;
}
}
if (usepixmap) {
painter.end();
m_textureID=w.graphView()->bindTexture(m_pixmap);
}
w.invalidate_xAxisImage=false;
}
if (usepixmap && !m_pixmap.isNull()) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
w.graphView()->drawTexture(QPoint(left-20,(top+height)-m_pixmap.height()+4),m_textureID);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
} }
} }

View File

@ -43,5 +43,8 @@ class gXAxis:public Layer
bool m_fadeout; bool m_fadeout;
qint64 tz_offset; qint64 tz_offset;
float tz_hours; float tz_hours;
QPixmap m_pixmap;
GLuint m_textureID;
}; };
#endif // GXAXIS_H #endif // GXAXIS_H

View File

@ -31,10 +31,10 @@ gXGrid::~gXGrid()
} }
void gXGrid::paint(gGraph & w,int left,int top, int width, int height) void gXGrid::paint(gGraph & w,int left,int top, int width, int height)
{ {
gVertexBuffer * stippled, * lines;
int x,y; int x,y;
gVertexBuffer * stippled, * lines;
EventDataType miny=w.min_y; EventDataType miny=w.min_y;
EventDataType maxy=w.max_y; EventDataType maxy=w.max_y;
@ -129,6 +129,7 @@ gYAxis::gYAxis(QColor col)
{ {
m_line_color=col; m_line_color=col;
m_text_color=col; m_text_color=col;
yAxisImageTex=0;
m_yaxis_scale=1; m_yaxis_scale=1;
} }
@ -142,9 +143,9 @@ void gYAxis::paint(gGraph & w,int left,int top, int width, int height)
if (w.graphView()->usePixmapCache()) { if (w.graphView()->usePixmapCache()) {
if (w.invalidate_yAxisImage) { if (w.invalidate_yAxisImage) {
if (!w.yAxisImage.isNull()) { if (!yAxisImage.isNull()) {
w.graphView()->deleteTexture(w.yAxisImageTex); w.graphView()->deleteTexture(yAxisImageTex);
w.yAxisImage=QImage(); yAxisImage=QPixmap();
} }
@ -167,10 +168,10 @@ void gYAxis::paint(gGraph & w,int left,int top, int width, int height)
GetTextExtent(fd,x,y); GetTextExtent(fd,x,y);
yh=y; yh=y;
QPixmap pixmap(width,height+y+4); yAxisImage=QPixmap(width,height+y+4);
pixmap.fill(Qt::transparent); yAxisImage.fill(Qt::transparent);
QPainter paint(&pixmap); QPainter paint(&yAxisImage);
double max_yticks=round(height / (y+14.0)); // plus spacing between lines double max_yticks=round(height / (y+14.0)); // plus spacing between lines
@ -246,16 +247,16 @@ void gYAxis::paint(gGraph & w,int left,int top, int width, int height)
} }
} }
paint.end(); paint.end();
w.yAxisImage=QGLWidget::convertToGLFormat(pixmap.toImage().mirrored(false,true)); //yAxisImage=QGLWidget::convertToGLFormat(pixmap.toImage().mirrored(false,true));
w.yAxisImageTex=w.graphView()->bindTexture(w.yAxisImage); yAxisImageTex=w.graphView()->bindTexture(yAxisImage);
w.invalidate_yAxisImage=false; w.invalidate_yAxisImage=false;
} }
if (!w.yAxisImage.isNull()) { if (!yAxisImage.isNull()) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
w.graphView()->drawTexture(QPoint(left,(top+height)-w.yAxisImage.height()+5),w.yAxisImageTex); w.graphView()->drawTexture(QPoint(left,(top+height)-yAxisImage.height()+5),yAxisImageTex);
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }

View File

@ -112,6 +112,9 @@ class gYAxis:public Layer
gVertexBuffer * lines; gVertexBuffer * lines;
virtual bool mouseMoveEvent(QMouseEvent * event); virtual bool mouseMoveEvent(QMouseEvent * event);
QPixmap yAxisImage;
GLuint yAxisImageTex;
}; };
/*! \class gYAxisTime /*! \class gYAxisTime