1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-30 02:50:05 +00:00

Made the OpenGL graphics context keep a cache of textures it has recently used for Image rendering, to avoid repeatedly moving data to the GPU.

This commit is contained in:
jules 2014-02-20 16:38:49 +00:00
parent 95d9d489a1
commit 7a869d6528
9 changed files with 222 additions and 64 deletions

View file

@ -31,6 +31,12 @@ ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, co
ImagePixelData::~ImagePixelData()
{
listeners.call (&Listener::imageDataBeingDeleted, this);
}
void ImagePixelData::sendDataChangeMessage()
{
listeners.call (&Listener::imageDataChanged, this);
}
//==============================================================================
@ -69,15 +75,19 @@ public:
LowLevelGraphicsContext* createLowLevelContext() override
{
sendDataChangeMessage();
return new LowLevelGraphicsSoftwareRenderer (Image (this));
}
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
{
bitmap.data = imageData + x * pixelStride + y * lineStride;
bitmap.pixelFormat = pixelFormat;
bitmap.lineStride = lineStride;
bitmap.pixelStride = pixelStride;
if (mode != Image::BitmapData::readOnly)
sendDataChangeMessage();
}
ImagePixelData* clone() override
@ -146,6 +156,9 @@ public:
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
{
image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode);
if (mode != Image::BitmapData::readOnly)
sendDataChangeMessage();
}
ImagePixelData* clone() override

View file

@ -455,6 +455,19 @@ public:
typedef ReferenceCountedObjectPtr<ImagePixelData> Ptr;
//==============================================================================
struct Listener
{
virtual ~Listener() {}
virtual void imageDataChanged (ImagePixelData*) = 0;
virtual void imageDataBeingDeleted (ImagePixelData*) = 0;
};
ListenerList<Listener> listeners;
void sendDataChangeMessage();
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData)
};

View file

@ -52,15 +52,19 @@ public:
LowLevelGraphicsContext* createLowLevelContext() override
{
sendDataChangeMessage();
return new CoreGraphicsContext (context, height, 1.0f);
}
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
{
bitmap.data = imageData + x * pixelStride + y * lineStride;
bitmap.pixelFormat = pixelFormat;
bitmap.lineStride = lineStride;
bitmap.pixelStride = pixelStride;
if (mode != Image::BitmapData::readOnly)
sendDataChangeMessage();
}
ImagePixelData* clone() override

View file

@ -617,15 +617,19 @@ public:
LowLevelGraphicsContext* createLowLevelContext() override
{
sendDataChangeMessage();
return new LowLevelGraphicsSoftwareRenderer (Image (this));
}
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
{
bitmap.data = imageData + x * pixelStride + y * lineStride;
bitmap.pixelFormat = pixelFormat;
bitmap.lineStride = lineStride;
bitmap.pixelStride = pixelStride;
if (mode != Image::BitmapData::readOnly)
sendDataChangeMessage();
}
ImagePixelData* clone() override

View file

@ -328,15 +328,19 @@ public:
LowLevelGraphicsContext* createLowLevelContext() override
{
sendDataChangeMessage();
return new LowLevelGraphicsSoftwareRenderer (Image (this));
}
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
{
bitmap.data = imageData + x * pixelStride + y * lineStride;
bitmap.pixelFormat = pixelFormat;
bitmap.lineStride = lineStride;
bitmap.pixelStride = pixelStride;
if (mode != Image::BitmapData::readOnly)
sendDataChangeMessage();
}
ImagePixelData* clone() override

View file

@ -25,6 +25,171 @@
namespace OpenGLRendering
{
struct TextureInfo
{
GLuint textureID;
int imageWidth, imageHeight;
float fullWidthProportion, fullHeightProportion;
};
//==============================================================================
// This list persists in the OpenGLContext, and will re-use cached textures which
// are created from Images.
struct CachedImageList : public ReferenceCountedObject,
private ImagePixelData::Listener
{
CachedImageList (size_t totalCacheSizeInPixels = 8 * 1024 * 1024) noexcept
: totalSize (0), maxCacheSize (totalCacheSizeInPixels) {}
static CachedImageList* get (OpenGLContext& context)
{
const char cacheValueID[] = "CachedImages";
CachedImageList* list = static_cast<CachedImageList*> (context.getAssociatedObject (cacheValueID));
if (list == nullptr)
{
list = new CachedImageList();
context.setAssociatedObject (cacheValueID, list);
}
return list;
}
TextureInfo getTextureFor (const Image& image)
{
ImagePixelData* const pixelData = image.getPixelData();
CachedImage* c = findCachedImage (pixelData);
if (c == nullptr)
{
if (OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image))
{
TextureInfo t;
t.textureID = fb->getTextureID();
t.imageWidth = image.getWidth();
t.imageHeight = image.getHeight();
t.fullWidthProportion = 1.0f;
t.fullHeightProportion = 1.0f;
return t;
}
c = images.add (new CachedImage (*this, pixelData));
totalSize += c->imageSize;
while (totalSize > maxCacheSize && images.size() > 1 && totalSize > 0)
removeOldestItem();
}
return c->getTextureInfo();
}
typedef ReferenceCountedObjectPtr<CachedImageList> Ptr;
private:
void imageDataChanged (ImagePixelData* im) override
{
if (CachedImage* c = findCachedImage (im))
c->texture.release();
}
void imageDataBeingDeleted (ImagePixelData* im) override
{
for (int i = images.size(); --i >= 0;)
{
if (images.getUnchecked(i)->pixelData == im)
{
totalSize -= images.getUnchecked(i)->imageSize;
images.remove (i);
break;
}
}
}
struct CachedImage
{
CachedImage (CachedImageList& list, ImagePixelData* im)
: owner (list), pixelData (im),
lastUsed (Time::getCurrentTime()),
imageSize (im->width * im->height)
{
pixelData->listeners.add (&owner);
}
~CachedImage()
{
if (pixelData != nullptr)
pixelData->listeners.remove (&owner);
}
TextureInfo getTextureInfo()
{
TextureInfo t;
if (texture.getTextureID() == 0)
texture.loadImage (Image (pixelData));
t.textureID = texture.getTextureID();
t.imageWidth = pixelData->width;
t.imageHeight = pixelData->height;
t.fullWidthProportion = t.imageWidth / (float) texture.getWidth();
t.fullHeightProportion = t.imageHeight / (float) texture.getHeight();
lastUsed = Time::getCurrentTime();
return t;
}
CachedImageList& owner;
ImagePixelData* pixelData;
OpenGLTexture texture;
Time lastUsed;
const size_t imageSize;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
};
OwnedArray<CachedImage> images;
size_t totalSize, maxCacheSize;
CachedImage* findCachedImage (ImagePixelData* const pixelData) const
{
for (int i = 0; i < images.size(); ++i)
{
CachedImage* c = images.getUnchecked(i);
if (c->pixelData == pixelData)
return c;
}
return nullptr;
}
void removeOldestItem()
{
CachedImage* oldest = nullptr;
for (int i = 0; i < images.size(); ++i)
{
CachedImage* c = images.getUnchecked(i);
if (oldest == nullptr || c->lastUsed < oldest->lastUsed)
oldest = c;
}
if (oldest != nullptr)
{
totalSize -= oldest->imageSize;
images.removeObject (oldest);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImageList)
};
//==============================================================================
struct Target
{
Target (OpenGLContext& c, GLuint fbID, int width, int height) noexcept
@ -466,13 +631,13 @@ public:
imageLimits.set (fullWidthProportion, fullHeightProportion);
}
void setMatrix (const AffineTransform& trans, const OpenGLTextureFromImage& im,
void setMatrix (const AffineTransform& trans, const TextureInfo& textureInfo,
const float targetX, const float targetY,
bool isForTiling) const
{
setMatrix (trans,
im.imageWidth, im.imageHeight,
im.fullWidthProportion, im.fullHeightProportion,
textureInfo.imageWidth, textureInfo.imageHeight,
textureInfo.fullWidthProportion, textureInfo.fullHeightProportion,
targetX, targetY, isForTiling);
}
@ -1134,6 +1299,7 @@ public:
JUCE_CHECK_OPENGL_ERROR
activeTextures.clear();
shaderQuadQueue.initialise();
cachedImageList = CachedImageList::get (t.context);
JUCE_CHECK_OPENGL_ERROR
}
@ -1257,7 +1423,7 @@ public:
JUCE_CHECK_OPENGL_ERROR
}
void setShaderForTiledImageFill (const OpenGLTextureFromImage& image, const AffineTransform& transform,
void setShaderForTiledImageFill (const TextureInfo& textureInfo, const AffineTransform& transform,
const int maskTextureID, const Rectangle<int>* const maskArea, bool isTiledFill)
{
blendMode.setPremultipliedBlendingMode (shaderQuadQueue);
@ -1269,7 +1435,7 @@ public:
if (maskArea != nullptr)
{
activeTextures.setTwoTextureMode (shaderQuadQueue, image.textureID, (GLuint) maskTextureID);
activeTextures.setTwoTextureMode (shaderQuadQueue, textureInfo.textureID, (GLuint) maskTextureID);
if (isTiledFill)
{
@ -1287,7 +1453,7 @@ public:
else
{
activeTextures.setSingleTextureMode (shaderQuadQueue);
activeTextures.bindTexture (image.textureID);
activeTextures.bindTexture (textureInfo.textureID);
if (isTiledFill)
{
@ -1301,7 +1467,7 @@ public:
}
}
imageParams->setMatrix (transform, image, (float) target.bounds.getX(), (float) target.bounds.getY(), isTiledFill);
imageParams->setMatrix (transform, textureInfo, (float) target.bounds.getX(), (float) target.bounds.getY(), isTiledFill);
if (maskParams != nullptr)
maskParams->setBounds (*maskArea, target, 1);
@ -1315,6 +1481,8 @@ public:
StateHelpers::CurrentShader currentShader;
StateHelpers::ShaderQuadQueue shaderQuadQueue;
CachedImageList::Ptr cachedImageList;
private:
GLuint previousFrameBufferTarget;
};
@ -1434,8 +1602,7 @@ public:
const AffineTransform& trans, Graphics::ResamplingQuality, bool tiledFill) const
{
state->shaderQuadQueue.flush();
OpenGLTextureFromImage image (src);
state->setShaderForTiledImageFill (image, trans, 0, nullptr, tiledFill);
state->setShaderForTiledImageFill (state->cachedImageList->getTextureFor (src), trans, 0, nullptr, tiledFill);
state->shaderQuadQueue.add (iter, PixelARGB ((uint8) alpha, (uint8) alpha, (uint8) alpha, (uint8) alpha));
state->shaderQuadQueue.flush();

View file

@ -253,30 +253,3 @@ void OpenGLHelpers::fillRect (const Rectangle<int>& rect)
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
}
#endif
//==============================================================================
OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image)
: imageWidth (image.getWidth()),
imageHeight (image.getHeight())
{
JUCE_CHECK_OPENGL_ERROR
if (OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image))
{
textureID = fb->getTextureID();
fullWidthProportion = 1.0f;
fullHeightProportion = 1.0f;
}
else
{
texture = new OpenGLTexture();
texture->loadImage (image);
textureID = texture->getTextureID();
fullWidthProportion = imageWidth / (float) texture->getWidth();
fullHeightProportion = imageHeight / (float) texture->getHeight();
}
JUCE_CHECK_OPENGL_ERROR
}
OpenGLTextureFromImage::~OpenGLTextureFromImage() {}

View file

@ -97,29 +97,5 @@ public:
#endif
};
//==============================================================================
/**
Used as a local object while rendering, this will create a temporary texture ID
from an image in the quickest way possible.
If the image is backed by an OpenGL framebuffer, it will use that directly; otherwise,
this object will create a temporary texture or framebuffer and copy the image.
*/
class JUCE_API OpenGLTextureFromImage
{
public:
OpenGLTextureFromImage (const Image& image);
~OpenGLTextureFromImage();
GLuint textureID;
const int imageWidth, imageHeight;
float fullWidthProportion, fullHeightProportion;
private:
ScopedPointer<OpenGLTexture> texture;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTextureFromImage)
};
#endif // JUCE_OPENGLHELPERS_H_INCLUDED

View file

@ -40,6 +40,7 @@ public:
LowLevelGraphicsContext* createLowLevelContext() override
{
sendDataChangeMessage();
return createOpenGLGraphicsContext (context, frameBuffer);
}
@ -73,6 +74,9 @@ public:
case Image::BitmapData::readWrite: DataReleaser<Reader, Writer>::initialise (frameBuffer, bitmapData, x, y); break;
default: jassertfalse; break;
}
if (mode != Image::BitmapData::readOnly)
sendDataChangeMessage();
}
OpenGLContext& context;