Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / TexPlay image creation/loading slow
- - By Spooner Date 2010-08-14 09:25
If using TexPlay, whenever a Gosu::Image is loaded from disk, created (Texplay::create_blank_image) or duplicated (Gosu::Image#dup), Texplay caches that image so that it can be manipulated by drawing commands. The problem with this is that it is a rather slow process and will either slow frame rate if an image is created during the game or lead to an extra several second delay if lots of images are generated at once, for example when loading a new level.

I realise that it is probably impossible to shorten the time necessary to perform this caching, but I think there may be an option available to reduce this time in certain circumstances. Not saying that directly optimising the caching wouldn't be greatly appreciated, of course :)

When creating a blank image or duplicating an existing image, there is no reason not to immediately cache it, since there is nothing else to use it for except for Texplay manipulation. However, when loading an image from disk, it may be being intended to use only with Gosu::Image#draw, rather than be manipulated with TexPlay, so perhaps having a :caching option added to Gosu::Image#new, with values :auto, :lazy (cache only when first TexPlay operation is applied to the image) & :no? Alternatively, a global TexPlay::caching attribute would be fine, I think.

In my project, I have some images that will just be used with vanilla Gosu and some that will be manipulated with Texplay, so being able to control when, and if, my images are cached would make a significant difference to level changing delays (I don't need to create/load images in-game, but I do need to load quite a lot of new "static" images when changing levels, 100 or more, but significantly less "dynamic" images).
Parent - - By banister Date 2010-08-14 10:42
you are right, this issue has been raised before and i'll work on adding this functionality very soon :)
Parent - - By Spooner Date 2010-08-14 10:51 Edited 2010-08-14 10:54
Thanks! I just thought I'd remind you in writing, since I think I mentioned it in IRC a couple of weeks ago and I've seen other mentions of it on the forumv (but not a direct feature request or workaround discussion). I monkey-patched to disable caching so there isn't a massive rush for myself :$
Parent - - By banister Date 2010-08-15 15:05
I've currently implemented this API in the git repo:

TexPlay.set_options :caching => true

Gosu::Image.new(window, "1.png", :caching => false)

So you can turn caching on and off globally with TexPlay.set_options, or on or off locally as you load a given Image

I've just implemented true or false values, something like :lazy would require messing with the C code (which i may or may not do at some point :))

Let me know if you have any alternative API ideas
Parent - - By Spooner Date 2010-08-15 15:21
That sounds fine; thanks!

I'm not sure the :lazy would have been useful for many people really. Thinking about it, the easier way to manage this, in pure Ruby, would be Image#cached? and a Image#cache just in case you want to cache just-in-time manually (drawing operations needn't check this value, since, as you say, that would mean lots of C changes). I don't think those are critical additions, though and again, I'm not sure if there are use-cases (so good idea not to implement them :$).

I assume that Texplay::create_image is always cached, regardless of options, but that Image::dup follows the global option? What about images from Devil::to_gosu? (sorry, I am picky, though it is a useful skill to have occasionally).

What exactly can you not do with an un-cached image? I assume it just won't work with any Texplay functions at all (or act oddly)? No exceptions?
Parent - By banister Date 2010-08-16 04:39 Edited 2010-08-16 11:34
Image#dup doesn't need a :caching option as internally it involves a #splice over the whole image, which will cause TP to overwrite any junk data that may be in the cache. TexPlay.create_image may NOT need a refresh_cache, because really it's just a large blank (or colored with the :color option to create_image) filled rect over the entire image, so it too will overwrite any junk data. I think I can safely get rid of the refresh_cache in create_image(), that should make dynamic image creation/manipulation at runtime substantially faster.

Ok, there already is a #cached? method, but it's called Image#quad_cached? This is because gosu images (if they're < MAX_TEXTURE_SIZE x MAX_TEXTURE_SIZE) share territory on a single opengl texture; so you're actually checking if the entire opengl texture is cached. If you look at the prepare_image() method in texplay.rb, you'll see that #quad_cached? is already being used to determine if refresh_cache is necessary http://github.com/banister/texplay/blob/master/lib/texplay.rb#L154 )

I wrote somewhere an explanation for why the refresh_cache appears in Gosu::Image.new, here it is:
"90% of the time you will be able to use TexPlay just fine without the automatic caching in the Gosu::Image.new method...however in certain rare situations (see below) TexPlay will not do what you want unless auto-caching is enabled.

The rare situation is when two or more Gosu images are stored internally on the same quad yet you invoke a TexPlay drawing method on one of those images BEFORE you've finished loading the other images.. In this situation all TexPlay manipulations will work fine for the images loaded prior to the first TexPlay drawing call (TexPlay lazily caches the quad on a drawing action if the quad is not already cached) but will produce weird results for all image manipulation on the images loaded AFTER (since they missed out on being cached.)
"

So, so long as you load all images at the start of the game, you do not need refresh_cache at all. The difficulty only arises where some images are loaded at runtime, after the other images that share their opengl texture have already been cached. So yeah, in most cases you wont need the #refresh_cache at all.

The reason #refresh_cache is so expensive is that it's caching the whole quad, so in most cases this requires downloading a 1024x1024 image from video memory and this is a slow process. It is impossible (afaik) to just download a portion of an opengl texture...though it IS possible to upload a portion of an opengl texture (see glTexSubImage2D). This is why syncing is relatively fast (and so manipulating images at runtime is viable) but caching is slow and prohibitive).

One strange point you may note from looking at prepare_image() is that #refresh_cache is called IF #quad_cached? returns true, rather than if it returns false (as you'd expect). This is because if it returns false we dont have to worry about it as TP will lazily cache it on first drawing action and everything will be fine. However if it returns true then the cached data is out of date, as the quad was cached BEFORE the new image was loaded and so excludes the new image data; hence we have to refresh it.

Notwithstanding all of the above, i think it is possible to still do things better -- maybe an image can recognize that the cache is out of date but postpone refreshing it until a drawing action is first performed. Still though, this doesn't really help things, as the bottleneck -- i.e downloading a huge 1024x1024 image from video memory must still be done at some point.

One technique i use myself, to get around this slow caching process, is to reuse old images. So rather than doing a TexPlay.create_image() at runtime, I just clear out an old image that has already been cached by doing something like: old_image.rect 0, 0, width, height, :fill => true, :color => :alpha, and then drawing over it as usual.

TL;DR
With the removal of the refresh_cache call in TexPlay.create_image()  creating/manipulating images at runtime using TexPlay.create_image() should be significantly faster. However the bottleneck still exists when loading a normal image from a file at runtime (i.e not using create_image). But this bottleneck disappears when you do not intend to manipulate the image (load with :caching => false). In the case where we do need to manipulate the loaded image though, I cannot think of any way to get around needing to refresh_cache and so downloading a 1024x1024 texture from video memory, I think this will always be slow. But now that i've restricted the stituations where this (refresh_cache) happens I hope it's not such a big deal anymore.
Parent - By banister Date 2010-08-16 07:33
Ok, i pushed a new 0.2.980 gem, that has all this caching stuff.

(1) adds :caching option (to both Gosu::Image.new and TexPlay.set_options... remember it only accepts true and false)
(2) gets rid of auto-caching for TexPlay.create_image
(3) also added TexPlay#clear method, it essentially  just sticks a filled rect over an image of a set color, e.g
     img.clear :color => :red, :dest_select => :transparent
     is equivalent to:
     img.rect 0,0, width, height, :fill => true, :color => :red, :dest_select => :transparent
Up Topic Gosu / Gosu Exchange / TexPlay image creation/loading slow

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill