(Solved) RenderTexture is very slow on 3.7+ versions

We have a game that requires RenderTexture to be called every frame that captures the screen of the game. I was previously using version 3.6 and was getting around 30 fps on windows 8.1 (Microsoft lumia 535). I had to update the engine so that i could start building universal windows 10 apps. I tried every version after 3.6 and they all produces the same result. The RenderTexture slows down the program so much it becomes unplayable. does any one else having problems similar to what we having?

Edit. Restarted a new project to demonstrate the issue.

Hello world.h

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__


#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
	cocos2d::Size _WorldSize;
public:
	void UpdateRenderer(float dt); 
	LayerColor * layer1;
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};


#endif // __HELLOWORLD_SCENE_H__

and the hellworld.cpp file

#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...
	// added for testing. Creating a Layer color that will hold bunch of sprites for testing.
	layer1 = LayerColor::create(Color4B(255, 255, 255, 255), 1, 1);


	//added for testing. creating bunch of sprites to test rendertexture.
	auto sprite1 = Sprite::create("lvl1prt1.png");
	auto sprite2 = Sprite::create("lvl1prt2.png");
	auto sprite3 = Sprite::create("lvl1prt3.png");
	auto sprite4 = Sprite::create("lvl1prt1Floor.png");
	auto sprite5 = Sprite::create("bottomEdge.png");
	auto sprite6 = Sprite::create("rightEdge.png");

	layer1->addChild(sprite1);
	layer1->addChild(sprite2);
	layer1->addChild(sprite3);
	layer1->addChild(sprite4);
	layer1->addChild(sprite5);
	layer1->addChild(sprite6);



    // add a label shows "Hello World"
    // create and initialize a label
	auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);

	// position the label on the center of the screen
	label->setPosition(Vec2(origin.x + visibleSize.width / 2,
		origin.y + visibleSize.height - label->getContentSize().height));

	// add the label as a child to this layer
	this->addChild(label, 1);

	auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer

	this->addChild(sprite, 0);
    this->addChild(layer1, 0);


	_WorldSize = Director::getInstance()->getVisibleSize();
    
	//schedule to updateRenderer
 	SEL_SCHEDULE selector = schedule_selector(HelloWorld::UpdateRenderer);
	this->schedule(selector, 1.00 / 60.0);


	

    return true;
}

void HelloWorld::UpdateRenderer(float dt)
{

	//calling render texture
	auto renderTexture = RenderTexture::create(_WorldSize.width, _WorldSize.height, Texture2D::PixelFormat::RGBA8888);
	renderTexture->begin();
		layer1->visit(); //rendering layer1 and its children by calling visit.
	renderTexture->end();
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

The above code tested as windows 8,1 phone app. Running the above code on pc doesn’t show much issues (perhaps the pc is too good not sure why) However testing on Microsoft Lumia 640 LTE provided a very bad frame rate. there is a huge (about half a 200ms) delay when the rendertexture code is executed. If you run on debug mode you will only deal with delay. However when you try it on Release,
The number of vertices increases for a while and then app crashes.

I get a bunch of exeptions in the output window. for a while
Exception thrown at 0x76E0E22B in Test.WindowsPhone.exe: Microsoft C++ exception: _com_error at memory location 0x03EBF300.

Then i get the following error message
Exception thrown at 0x00F71ECB in Test.WindowsPhone.exe: 0xC0000005: Access violation reading location 0x00000000.

I am not sure if its me me screwing things up or if there is a serious issue with the rendertexture. I used the Version 3.10 on this test. Later on will upload the same code test from Version 3.6

ok. here is the same code running on cocos2d-x3.6. and its in Release mode. I am getting 60fps with no problem.

I am desperate to find a solution for this. if anyone has any idea please let me know. As I mentioned, the problem exist on all the versions after 3.6. so thats cocos2dx 3.7 upto cocos2dx 3.10 . If I want to develop for windows 10 universal app, i have to use one of these versions since 3.6 is not supported.

I think i may have sold the problem although i am not really sure if this will produce any other problems but it works fine for me so far.

There are two things that seems to mess things up. The _groupCommand within the CCRenderTexture and the _trianglesCommand within the Sprite class. Please note that this for cocos2dx V3.10 so not sure if this would work on older versions.

Firstly I commented out the following codes from RenderTexture::begin()

 Renderer *renderer =  Director::getInstance()->getRenderer();
 renderer->addCommand(&_groupCommand);
 renderer->pushGroup(_groupCommand.getRenderQueueID());

Also had to comment out the

 renderer->popGroup();

within the RenderTexture::end()

Secondly had to modify the void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) so that it was using old Quadcommand instead of the triangleCommand.

before we do that, make sure to #include "renderer/CCQuadCommand.h" inside the CCSprite.h and add the a protected member QuadCommand _quadCommand;

now change the following code inside the void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)

 _trianglesCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags);
 renderer->addCommand(&_trianglesCommand);

into this;

_quadCommand.init(_globalZOrder, _texture->getName(),
 getGLProgramState(), _blendFunc, &_quad, 1, transform, flags);
 renderer->addCommand(&_quadCommand);

and here is a screenshot before making the changes above. The game couldn’t even pass the intro scene before freezing .

and after screenshot where everything seems to work fine.

Hi Guys,

Apologies for bringing back this question from the dead.

We had some issue since the release of cocos2d-x 3.3 release with our drawing app on windows phone. Implementing the suggestions given in the scenario 1 fixed the issue.

As we are still on the cocos2d-x 3.3, the scenario number 2 is not applicable to us. Only the points listed in the scenario 1 in which the GroupCommand, pushGroup and popGroup are commented is applicable for us.

Just to find out what could be causing the issue, I have traced all code related to the RenderTexture, GroupCommand, GroupCommandManager, Renderer::render, visitRenderQueue and processRenderCommand etc.

Fortunately, now I have a pretty good idea how this code is working. But unfortunately, I couldn’t find anything that maybe fixing the issue. As per my understanding, as soon as the group command is created, it gets allocated with a render queue id. Soon as we push new group with this generated renderQueueID, a new queue is created and new RenderCommands are being pushed to this new queue to execute in a batch.

Now, I don’t see how commenting the code in the scenario 1 should have any effect on the speed but it does.

May somebody with the better understanding about it please explain whats going beneath that I may be missing.

Just for reference (commenting the code below fixing the issue):

RenderTexture::begin()
{
    ........
    Renderer *renderer =  Director::getInstance()->getRenderer();
    renderer->addCommand(&_groupCommand);
    renderer->pushGroup(_groupCommand.getRenderQueueID());
    .......
}
RenderTexture::end()
{
    .......
    renderer->popGroup();
    .......
}

Please help me understand.

Thanks and Regards,

  • ManishK
2 Likes

I also like to second this. While this fixes the problem, it sure doesn’t feel like a good solution. I don’t know what sort of other issues it may cause. It would be nice to get some more info from more experienced users. ( please let me also emphasise this, I only encountered this issue on windows phone devices. Not simulators. actual devices.)

2 Likes

Anyone here from the cocos2dx team please?

Hi @Yucel,
I was facing exactly the same problem - but on Android. On iOS everything was working perfectly. Based on your workaround, I could find a reason for this behavior and developed a fix - see a pull request on Github.

1 Like