How to only draw a part of a sprite?

Hey :slight_smile:

I have that huge image that impact a lot the performances when displayed because of it size and I was wondering if it was possible to only draw a part of it (just the visible part on the screen) to improve performances.
Is this possible by creating a subclass of Sprite and overriding the draw method ?

Thank you very much for you help :slight_smile:

You could absolutely achieve it by subclassing Sprite. But I think it’s easy to create small Sprite from large image by using static Sprite* create(const std::string& filename, const Rect& rect); method.

The problem is that my sprite is used in a parallax, so I would need to recreate it for every frame.
I’m looking for a way to do it by overriding the draw method but I can’t find anything about that…

may be you can get something from this post . About how to use clipping node.

This example is for a layer, I wanna do it for a Sprite…

So I guess, no one know how to override the draw method to make it :frowning:

Does anyone know how I can do that ?

I tried everything but nothing worked. I put my images in a CCClippingNode but this doesn’t change anything, I subclassed Sprite and tried to use GL_SCISSOR_TEST but it’s not working… :confused:
Maybe there is a way to do it with OpenGL faster but everything I tried didn’t work… :frowning:

I’m kind of desperate, is there a way for cocos2d-x to handle big images ?

To clarify how big is this big texture?

The size of the quad on screen probably isn’t what’s causing the performance issue but rather the size of the texture being pushed to the GPU. I would recommend splitting the image into multiple textures and placing them side by side in your paralax layer as separate sprites. This way you can cull the pieces not on screen and avoid pushing texture data that won’t be rendered to GPU.

Why aren’t you using a Rect to do this?

almax27 > The texture is 2048x1024 pixels.
I’m gonna try that and let you know if the performance are better. When you’re talking about culling, you mean hide the sprites that are not visibles on the screen ? Does cocos2d-x handle that or do I have to do it by subclassing the Sprite class ?

slackmoehrle > What do you mean exactly ?

Yes, culling means not rendering objects that are off screen, if you’re using Cocos2d-x v3 or later, culling of sprites will be handled for you.

That resolution shouldn’t be an issue if you’re only rendering it once per frame with the standard sprite shader. Performance issues can come from any number of places in your code. If it’s definitely this one sprite that is causing you issues I’d recommend profiling your application to find the bottleneck.

Probably it shouldn’t be an issue but the thing is that I have two 2048x1024 images for my parallax. When I only enable one plan, everything is fine, but when I enable both, my game is running around 50fps on the first iPad. I really would like to have 60fps on that device and I’m sure there is a way to do it.

Do you really think divide the sprite into multiples sprites could help ? Do I have to split my PNGs file or can I directly instantiate a sprite taking a part of my background ?

You’ll have to split your pngs. Taking just a section of the image still pushes the whole texture to the GPU and updates the UV coordinates of the quad to map to an area of the the texture instead of the whole thing.

Also another option is to lower the resolution of your images, if it’s just background textures without too much detail you may be able to get away with halving them to 1024x512 and scaling the sprite up (or use setContentSize).

I was thinking something like: static Sprite* create(const std::string& filename, const Rect& rect); It looks like Owen mentioned the same thing above too.

Smaller Sprites means less for the GPU. I think this is always a good thing.

Okay, thank you very much, I’m gonna try this way and tell you if I see improvements.

Okay, so I just wanted to let you know that dividing my sprite programmatically with : static Sprite* create(const std::string& filename, const Rect& rect); doesn’t change anything.
The performance is exactly the same.

Should I try to cut my PNGs ? I don’t think it’s gonna change anything because cocos2d-x Sprite object seems to handle culling and everything automatically.

Another thing that is off topic but that could be my problem. I heard that cocos2d-x doesn’t handle well *.tmx map while my map is like 250x50 tiles big. Do you guys know any way to improve my map rendering performance ?

The latest release (3.2RC0) has a much improved tilemap renderer, so upgrade to FastTMX* and see if that helps or fixes your issue.

So are you using both two large background images in parallax behind a large tilemap? If so what does your tilemap consist of in terms of # layers and tileset image size dimensions? It may be the tilemap that was causing FPS issues, or the combination of both, especially if you have multiple layers and tilesets in your .tmx map.

As @almax27 mentioned the lower resolution scaled up is a great optimization for iPad1, or any device you find can’t support 60fps. Slicing the image into 4 PNGs for example. Either into 4 equal rect images if you can parallax in both directions, otherwise slice into 4 horizontal or vertical slices if your parallax is restricted to one axis.

With the ipad1 you can also consider a lower GPU texture quality, say 16-bit or less. You can even look into using PVR format for iOS devices. Throw in a quick test and surround your texture load with this and see if it helps. You can change the format for a single texture load, or all textures by setting in your App Delegate.

// Use on of these to force loading into 16-bit texture
// for opaque textures
Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGB565);
// for full alpha textures 
//Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGBA4444);
// for single alpha textures (unsure if all devices support)
//Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGB5A1);

// load texture from image (could also load texture atlas and add plist of spriteframes instead)
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage("image.png");
Sprite *sprite = Sprite::createWithTexture(texture);

// revert back to 32-bit textures by default (optional)
Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGBA8888);

If you haven’t run the OpenGL profiler in Instruments you may want to check that out as well.

I’ve got similar problem on old and new tmx maps. There is not enough memory to render map with 2000x2000 tiles with full filling and 3 layers on it. Looks like TMXLayer trying to allocate total number of quads before culling in TMXLayer::updateTotalQuads.
Is it correct, or I’m doing something wrong?

Yes, the current TMXTiledMap and TMXLayer do not handle large maps well - 120x120 with 3 layers is a “soft” max for decent performance. The latest experimental TMXTiledMap (named FastTMXTiledMap until recently, now it’s in cocos2d::experimental::TMXTiledMap) may handle your large map with decent performance.

Assuming 32-bits per tile a 2000x2000x3 map is 48MB. If memory/performance is an issue for your project you may also want to think about sub-dividing the map into screen-sized chunks. You may have to manage this yourself writing code to load in, for example, 9 screen maps (so player can move outside screen boundaries in any 8 directions, especially if camera follows them).

I’m unsure if the new experimental tiled map in 3.2 does any of this for you.

As far as the 3.x auto-culling support and performance, well, culling 12000000 tiles every frame is a large CPU workload, so the FastTMXTiledMap or your custom renderer should only send the tiles in view to the cocos2d render pipeline.

There is streaming tilemap support in KoboldKit may now be open sourced and the community could look at enhancing the FastTMXTiledMap with ideas from it if FastTMXTiledMap doesn’t already support chunking and tmx file streaming.

There is no way right now to handle large maps with cocos2d-x. Even the recent cocos2d::experimental::TMXTiledMap doesn’t do the job well. I wish we could have a better TiledMap object on cocos2d-x.