Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: "Huge Sprites"  (Read 23916 times)

0 Members and 1 Guest are viewing this topic.

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« on: December 11, 2008, 07:17:39 pm »
Sometimes there is the need to load a really big image, for a background for example.

As sf::Image uses a texture that is graphics-card-dependent... you're limited to the texture limit size there.
(e.g.: ye olde GeForce2ee can't go bigger than 512x512 textures if I'm correct)

So the need to load and slice the image arises, having a bunch of images and sprites to handle.
If this could be transparent to the user it would be really cool.

I'm going to implement this out of need and I'll post the code if you think it would be good to add it to the library.

-Martín

dorkfish

  • Newbie
  • *
  • Posts: 38
    • View Profile
"Huge Sprites"
« Reply #1 on: December 11, 2008, 08:09:02 pm »
I think this is a fantastic idea, go for it. :)

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« Reply #2 on: December 11, 2008, 11:20:03 pm »
Searching some code I found this:

"if (Adjust && myIsSmooth) " (see below)

Why is image smoothness needed for the half texel adjustment?
And what's that adjustment anyway? :D

--------------------------------------

@ Image.cpp
Code: [Select]

////////////////////////////////////////////////////////////
/// Convert a subrect expressed in pixels, into float
/// texture coordinates
////////////////////////////////////////////////////////////
FloatRect Image::GetTexCoords(const IntRect& Rect, bool Adjust) const
{
    float Width  = static_cast<float>(myTextureWidth);
    float Height = static_cast<float>(myTextureHeight);

    if (Adjust && myIsSmooth)  
    {
        return FloatRect((Rect.Left   + 0.5f) / Width,
                         (Rect.Top    + 0.5f) / Height,
                         (Rect.Right  - 0.5f) / Width,
                         (Rect.Bottom - 0.5f) / Height);
    }
    else
    {
        return FloatRect(Rect.Left   / Width,
                         Rect.Top    / Height,
                         Rect.Right  / Width,
                         Rect.Bottom / Height);
    }
}



Wizzard

  • Full Member
  • ***
  • Posts: 213
    • View Profile
"Huge Sprites"
« Reply #3 on: December 11, 2008, 11:50:38 pm »
Here is an article on it.

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« Reply #4 on: December 12, 2008, 03:17:19 pm »
Quote from: "Wizzard"
Here is an article on it.


Thanks a lot!

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« Reply #5 on: December 13, 2008, 12:20:14 am »
While reading the code, I found out that each sf::Image also keeps a local (system memory) copy of the pixels.

I think this is a big overhead for images not modified in-game (which is the most common case in sprites).

Maybe an option to disable that cachéd pixels, also disabling SetPixel, SaveFile, et cetera... or implementing a slow "get from opengl" version (I don't know if thats possible thou, but it should).

I'm implementing this caché-flag in the proof-of-concept "BigImage/BigSprite" classes I'm coding, because the target is a background for a game I'm developing (4000x1000 px RGBA), and that's roughly  15 Megabytes, which is useless to have it on system memory as I'm not modifying it.


Also, just calling "myPixels.clear()" doesn't change the vector's capacity (see vector::capacity()), just the size is set to 0... so memory is not freed at all.



Well, what do you think?

Imbue

  • Full Member
  • ***
  • Posts: 104
    • View Profile
"Huge Sprites"
« Reply #6 on: December 13, 2008, 01:05:03 am »
Quote from: "nitram_cero"
While reading the code, I found out that each sf::Image also keeps a local (system memory) copy of the pixels.
I'm not 100% sure, but I don't think you can trust the graphics card to store images for very long (like hitting alt-tab may wipe the memory).

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« Reply #7 on: December 13, 2008, 01:15:01 am »
Thanks for the reply, I haven't thought of that but I think you're correct.



I was looking around for a solution to vector::clear() and I found that doing a swap for a new, empty vector could exchange the internal array and then as the object dies, it's really freed.

myPixels.swap(vector<Color>());

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
"Huge Sprites"
« Reply #8 on: December 13, 2008, 11:07:25 am »
You're right, storing the pixel data in memory is often useless. However it's a different and complex topic, which would requires more than just a workaround in the code. You should focus on your primary goal, in my opinion.

The swap trick is indeed the only way to free the memory allocated by a vector ;)
Laurent Gomila - SFML developer

zarka

  • Jr. Member
  • **
  • Posts: 81
    • View Profile
"Huge Sprites"
« Reply #9 on: December 13, 2008, 01:11:47 pm »
Quote from: "Imbue"
Quote from: "nitram_cero"
While reading the code, I found out that each sf::Image also keeps a local (system memory) copy of the pixels.
I'm not 100% sure, but I don't think you can trust the graphics card to store images for very long (like hitting alt-tab may wipe the memory).


I belive that is only a problem on DirectX version <= 9.
OpenGL and DX10 should not have that problem.
//Zzzarka

Ceylo

  • Hero Member
  • *****
  • Posts: 2325
    • View Profile
    • http://sfemovie.yalir.org/
    • Email
"Huge Sprites"
« Reply #10 on: December 13, 2008, 02:42:29 pm »
Quote from: "Imbue"
I'm not 100% sure, but I don't think you can trust the graphics card to store images for very long (like hitting alt-tab may wipe the memory).

If this was to happen, how would SFML notice this ? To my mind, there would be no way.

But anyway, the images are stored in the graphic card memory through an OpenGL context, so until you destroy this context, your images are kept in the graphic card memory. Therefore, as SFML never destroy the main shared OpenGL context, alt-tab can't destroy your images until you explicitely choose to do so.
Want to play movies in your SFML application? Check out sfeMovie!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
"Huge Sprites"
« Reply #11 on: December 13, 2008, 02:46:45 pm »
Quote
But anyway, the images are stored in the graphic card memory through an OpenGL context, so until you destroy this context, your images are kept in the graphic card memory. Therefore, as SFML never destroy the main shared OpenGL context, alt-tab can't destroy your images until you explicitely choose to do so.

Absolutely :)
Laurent Gomila - SFML developer

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« Reply #12 on: December 13, 2008, 06:35:49 pm »
Then why keep the pixels inside the image class on system memory besides SetPixel/SaveToFile?
Most games never modify an Image in-game.

Also, I repeat, there is no freeing with "clear()"...
The code should be "myPixels.swap(std::vector<sf::Color>());" and that's it.

Thanks
-Martín

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
"Huge Sprites"
« Reply #13 on: December 13, 2008, 07:01:44 pm »
Quote
Then why keep the pixels inside the image class on system memory besides SetPixel/SaveToFile?

For SetPixel/SaveToFile (and all others).
You're right to say that, but that's just your point of view. SFML is not only made for games using static sprites, it can be used for a lot more purposes, which may include more complex image handling.
Adding an option not to store pixels in memory would require a smarter modification of SFML public interface.

Quote
Also, I repeat, there is no freeing with "clear()"...
The code should be "myPixels.swap(std::vector<sf::Color>());" and that's it.

myPixels.clear() is only called after an error. In such case, keeping the image instance alive obviously means it will be reused, so keeping the memory allocated makes sense. I don't see the benefits of clearing the memory in this function.
Laurent Gomila - SFML developer

nitram_cero

  • Full Member
  • ***
  • Posts: 166
    • View Profile
"Huge Sprites"
« Reply #14 on: December 13, 2008, 07:41:05 pm »
I think it's quite easy to add a "DoesCachePixels()/SetCachePixels(bool)" to the image interface, it's trivial (As "IsSmooth()/SetSmooth()")

In the case of not-caching, swap would be the only way to really free (as and only if wanted) that myPixels member after the CreateTexture().

I'm doing a BigImage class, that is multi-texture container similar to sf::Image for images larger than GL's max texture size.
I'm doing it to load three 15 Megabyte (uncompressed) background and making it easy (also implementing a BigSprite derived from sf::Drawable).

Keeping that 45 megs it's not necessary at all.

Quote
You're right to say that, but that's just your point of view.

Well, I know... I said it.
But I think you got me wrong (it's probably really hard to keep up reading line by line each post of the forum).

I know that it's useful to keep the pixels in some contexts. But in others it's not, so forcing it to keep them is not a solution neither.
Maybe I want to have 100 static sprites and 40 modifiable ones (for blood splatter for example, applied directly to the image).

It's ok for small (200x100, 80KB) sprites, but for not-so-big backgrounds (1000x1000, 4MB) it's quite a lot of memory waste.

I don't want to sound as a smart-ass, I'm just trying to contribute... so don't take it personal, man.

--------------------------
In
"void Image::SetSmooth(bool Smooth)"

It could check if "myIsSmooth != Smooth" before doing all the bind-change parameter-etc, to know if it's relevant and save some CPU time.
Even if it's not really important as you wont be calling it a lot, it's a good programming habit.

You only need to add an if clause.

---------------------------
    void EnsureTextureUpdate() const;
Should be public, as I may want to modify images and batch-update them before the first time they get binded (that could hog the CPU in different moments).