From c54ca8bf402c1b021958eb8989f5fb90d4389de4 Mon Sep 17 00:00:00 2001 From: Sam Hocevar <sam@hocevar.net> Date: Wed, 6 Mar 2013 15:20:00 +0000 Subject: [PATCH] image: try to reuse existing images when loading them, and don't unload tilesets yet, but make sure the architecture will allow it. --- src/image/image-private.h | 34 +++++++++------- src/image/image.cpp | 81 +++++++++++++++++++++++++++++++-------- src/lol/image/image.h | 2 + 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/image/image-private.h b/src/image/image-private.h index 77a7b60d..439649fb 100644 --- a/src/image/image-private.h +++ b/src/image/image-private.h @@ -19,17 +19,17 @@ namespace lol { -class ImageLoader +class ImageCodec { - friend class Image; + friend class ImageLoader; friend class ImageData; public: ImageData *(*fun)(char const *path); - ImageLoader *next; + ImageCodec *next; int priority; - static void RegisterLoader(ImageLoader *loader) + static void RegisterLoader(ImageCodec *loader) { Helper(loader); } @@ -37,7 +37,7 @@ public: private: static ImageData *Load(char const *path) { - ImageLoader *parser = Helper(nullptr); + ImageCodec *parser = Helper(nullptr); ImageData *ret = nullptr; while (parser && !ret) @@ -49,14 +49,14 @@ private: return ret; } - static ImageLoader *Helper(ImageLoader *set) + static ImageCodec *Helper(ImageCodec *set) { - static ImageLoader *loaders = nullptr; + static ImageCodec *loaders = nullptr; if (!set) return loaders; - ImageLoader **parser = &loaders; + ImageCodec **parser = &loaders; while (*parser && (*parser)->priority > set->priority) parser = &(*parser)->next; set->next = *parser; @@ -69,8 +69,15 @@ private: class ImageData { friend class Image; + friend class ImageLoader; public: + inline ImageData() + : m_size(0, 0), + m_format(PixelFormat::Unknown), + m_refcount(0) + { } + virtual ~ImageData() {} virtual bool Open(char const *) = 0; @@ -81,6 +88,7 @@ public: protected: ivec2 m_size; PixelFormat m_format; + int m_refcount; }; #define REGISTER_IMAGE_LOADER(name) \ @@ -88,12 +96,12 @@ protected: Register##name(); #define DECLARE_IMAGE_LOADER(name, prio) \ - template<typename T> class name##ImageLoader : public ImageLoader \ + template<typename T> class name##ImageCodec : public ImageCodec \ { \ public: \ - name##ImageLoader() \ + name##ImageCodec() \ { \ - static ImageLoader loader; \ + static ImageCodec loader; \ loader.fun = Load; \ loader.priority = prio; \ RegisterLoader(&loader); \ @@ -110,10 +118,10 @@ protected: } \ }; \ class name; \ - name##ImageLoader<name> name##ImageLoaderInstance; \ + name##ImageCodec<name> name##ImageCodecInstance; \ void Register##name() \ { \ - (void)&name##ImageLoaderInstance; \ + (void)&name##ImageCodecInstance; \ } \ class name : public ImageData diff --git a/src/image/image.cpp b/src/image/image.cpp index 2d2828ce..9d3c655c 100644 --- a/src/image/image.cpp +++ b/src/image/image.cpp @@ -20,18 +20,18 @@ using namespace std; namespace lol { +/* HACK: We cannot make this an ImageCodec member function, because the + * REGISTER_IMAGE_LOADER macro forward-declares free functions from + * the "lol" namespace. An apparent bug in Visual Studio's compiler + * makes it think these functions are actually in the top-level + * namespace when the forward declaration is in a class member function. + * To avoid the problem, we make the forward declaration in a free + * function. + * The bug was reported to Microsoft and fixed by them, but the fix + * is not yet available. + * https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */ static bool RegisterAllLoaders() { - /* HACK: We cannot make this an ImageLoader member function, because the - * REGISTER_IMAGE_LOADER macro forward-declares free functions from - * the "lol" namespace. An apparent bug in Visual Studio's compiler - * makes it think these functions are actually in the top-level - * namespace when the forward declaration is in a class member function. - * To avoid the problem, we make the forward declaration in a free - * function. - * The bug was reported to Microsoft and fixed by them, but the fix - * is not yet available. - * https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */ #if defined __ANDROID__ REGISTER_IMAGE_LOADER(AndroidImageData) #endif @@ -53,23 +53,72 @@ static bool RegisterAllLoaders() } /* - * Static image loader + * Our static image loader */ -Image *Image::Create(char const *path) +static class ImageLoader +{ +public: + Image *Create(char const *path); + void Destroy(Image *img); + +private: + Map<String, Image *> m_images; +} +image_loader; + +Image *ImageLoader::Create(char const *path) { /* Initialise loaders (see above) */ static bool init = RegisterAllLoaders(); UNUSED(init); - Image *ret = new Image(); - ret->m_data = ImageLoader::Load(path); - return ret; + /* Is our image already in the bank? If so, no need to create it. */ + Image *img; + + if (m_images.HasKey(path)) + { + img = m_images[path]; + } + else + { + img = new Image(); + img->m_data = ImageCodec::Load(path); + m_images[path] = img; + } + + ++img->m_data->m_refcount; + return img; +} + +void ImageLoader::Destroy(Image *img) +{ + ASSERT(img->m_data->m_refcount > 0); + if (--img->m_data->m_refcount == 0) + { + /* FIXME: unload images etc. here */ + /* XXX: we’re gonna hit a problem here because we didn’t keep + * the image path within the object, so unless we store it + * ourselves we’re good for a O(n) lookup… which we can’t do + * on Map objects anyway. */ + /* TODO: 1. remove image from Map + 2. delete img; */ + } +} + + +/* + * Static image methods + */ + +Image *Image::Create(char const *path) +{ + return image_loader.Create(path); } void Image::Destroy(Image *img) { - delete img; + return image_loader.Destroy(img); } /* diff --git a/src/lol/image/image.h b/src/lol/image/image.h index ea6499b3..cc2491ea 100644 --- a/src/lol/image/image.h +++ b/src/lol/image/image.h @@ -23,6 +23,8 @@ namespace lol class Image { + friend class ImageLoader; + public: static Image *Create(char const *path); static void Destroy(Image *img);