I might be missing some of the subtleties of your issue, but it sounds like you have a waveform (frequent/constant updating) being drawn below labels (moving but not often changed). I would create an image and draw all of the labels onto it directly. Whenever the labels change, clear the image and re-draw the new ones. Then in the actual paint callback, simply draw the image after the waveform. We use something similar in one of our plugins. Here's some code:
Image labelImage; (declared as a class variable in the header)
bool labelsChanged;
Hey, thanks for the reply. More info: I'm making a wave editor, and the text labels are for a grid to show regular points in time (e.g. 1s, 2s, 3s...) in the file. They only change when the user zooms and scrolls, but I'm want this to be super smooth and sexy.
Based on what you said, I think I might try caching the labels to images then drawing those. OpenGL shouldn't struggle with drawing 30 odd textured quads...right??
I've had something where I rendered the labels to an image, turned them into an OpenGL texture and handed it all off to OpenGL to deal with ... ? I don't know if that's the right solution ?
If you're rendering using an attached OpenGLContext, there actually shouldn't be any problems as images created on an active context should reside on the graphics card's memory as textures. It may not be an exact science, and you should probably test it.. 30 software images (small i suppose) should still be doable easily but it will obviously be slower than OpenGL.
And this was pretty slow due it calling glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fb) every frame (as profiling showed). So maybe I should keep everything in the pure OpenGL world?
Interesting. Yeah, building off of what I was talking about above with the single image, I'd also keep a pointer to its associated graphics context in memory so that you won't have to recreate it every frame but can still draw to it whenever the labels are updated. The main thing I was trying to emphasize was using a single image for all of the labels, drawing everything to it at once. Then you'd only draw that one image every frame, so there wouldn't be ~30 calls to drawImage(). Anyway, lots of ways to skin a cat and it's probably best to keep everything in pure OpenGL if you have the chops. Be sure to let us know what you chose and how it performs, since it's an interesting/common scenario.
Seconding what my teammate has said. Cache it to a single image, and leave it to update only when something large happens, like changing text colors or something similar.
Another thing to check when you're having redraw issues is a define in AppConfig.h:
Just remove the commented out line. That will rainbow-paint everything on your panel that is redrawing. Occasionally, what seems like a performance issue with one drawing component can occasionally be a more inisidious issue with a control that's redrawing constantly.
Also, if you're the same Rob Clouth, your new Hidden Structures EP is fantastic. I've been listening to it a lot this week!
Do you mean drawing each label that appears on screen to a large texture and storing the texture coord to pass into the shader? I like to avoid that if I can, just to avoid the hassle. But if the image-per-label thing is too slow I'll try that next.
I tried keeping a reference to the created context but it failed when trying to reuse it. Maybe I need to make it active or something. I'll try again.
Ok I've solved the text drawing speed using an cache of Images in a map, that maps the String to the Image.
struct CachedTextImage
{
String text;
int64 lastAccessed;
Image image;
};
map<String, ScopedPointer<CachedTextImage>> textImageMap;
typedef map<String, ScopedPointer<CachedTextImage>>::iterator TextImageMapIter;
Image BufferView::getTextAsImage(String text, Font font, Colour colour)
{
CachedTextImage* textImage = textImageMap[text];
if (textImage == nullptr)
{
// make the image
textImage = new CachedTextImage();
textImage->text = text;
textImage->lastAccessed = Time::currentTimeMillis();
int width = font.getStringWidth(text) + 5;
int height = font.getHeight();
textImage->image = Image(Image::ARGB, width, height, true);
Graphics& g = (Graphics)textImage->image;
g.setColour(colour);
g.setFont(font);
g.drawText(text, 0, 0, 50, 20, Justification::topLeft);
textImageMap[text] = textImage;
if (textImageMap.size() > 100)
{
// find oldest text image and remove it
CachedTextImage* oldestTextImage = textImage;
for (TextImageMapIter iter = textImageMap.begin(); iter != textImageMap.end(); iter++)
{
if (iter->second && iter->second->lastAccessed < oldestTextImage->lastAccessed)
oldestTextImage = iter->second;
}
textImageMap.erase(oldestTextImage->text);
}
}
return textImage->image;
}
This makes the text rendering much faster. But still the problem remains of recreating the context every frame with
createOpenGLGraphicsContext. I've tried creating it in newOpenGLContextCreated(), but if I try to use it in renderOpenGL() it fails trying to access a nullptr...has anyone tried this before?