RenderTexture not rendering small sprites on the left

Day of bugs, really:

string filename = "test.png";
auto renderTexture = RenderTexture::create(300, 240, Texture2D::PixelFormat::RGBA8888);
renderTexture->clear(0,0,0,0);
renderTexture->begin();
auto sprite = Sprite::create("0.png");
sprite->setPosition(Vec2(39, sprite->getContentSize().height * 0.5f));
sprite->visit();
renderTexture->end();
renderTexture->saveToFile(filename, Image::Format::PNG);
Director::getInstance()->getRenderer()->render();

It doesn’t render anything. If I change 39 to 40 it magically works (or add bigger image, big enough can be even at 0, 0). I’ve discovered that if image right side is below a certain value it won’t render to the renderTexture. Y position and height doesn’t matter.

Why?!

I’ve also tested this with DrawNode instead (using drawDot to render a simple dot) and it works fine.

Director::getInstance()->getRenderer()->render(); is suggested, but removing it doesn’t help.

Testing image (0.png) is 28x33. I’m attaching it to this post.

0

What platform (win/mac/ios) and cocos2d version?
It works for me using your image on Mac w/ver 3.17 cocos new project.

Note: the image is .jpeg on this forum so I did open it in an image editor to re-save as .png, not sure if there’s an issue with your original image.

iOS 12.1 running on iPad Pro 10.5, cocos2d-x 3.17.

image is a png on my machine, also I’ve got another one, just swapped with it for testing.

I’ve also uploaded it here, just in case: https://www113.zippyshare.com/v/vMkE0XeS/file.html

Okay, this is strange!

Today, I’ve made more tests.

  1. I’ve created fresh project and copy-pasted code here - it worked!
  2. I thought there may be a problem with some stuff in AppDelegate (mine is very custom), but nope, still worked
  3. I’ve copy-pasted HelloWorld scene to my existing project - it worked!
    Also I’ve added a quick code to preview saved file.

It looks like this:

string testPath = FileUtils::getInstance()->getWritablePath() + filename;
auto check = Sprite::create(testPath);
check->setPosition(Vec2(240, 160));
addChild(check);
  1. I’ve launched my old class with the same code again, but called it on init() - it worked!
  2. I’ve launched it using scheduleOnce() (5 seconds later) - still worked!
  3. In the non-working case I’ve called it when clicked a button (ui::Button class) - it still didn’t work - but I’ve added scheduleOnce with 0.0 time and guess what - it worked!

That’s very strange, because I don’t think addTouchEventListener works on different thread. However, as you can see, running it on the next frame fixes the issue.

  1. Then I’ve remembered - hey - let’s try default AppDelegate - and it worked! So I thought there’s something wrong with my AppDelegate. I’ve removed all unnecessary code and I ended up with pretty much the same. The only difference was the default designResolutionSize. Mine was 960x640, while default is 480x320.

I’ve created a new test project to show you how to reproduce this bug.

Classes.zip (6.6 KB)

Just create an empty cocos2d-x project, replace Classes and copy previously attached 0.png file (or use any other small png, it should work as well).

I haven’t looked at your new test project yet, but the “next frame” issue may just be due to the button event occurring before the render target save to file executes.

If so that’s not technically a bug, but more of a feature request, in the sense that it’d be working as intended, but is not the behavior you want? Or you just need to work within the behavior of the engine.

Anyway, I’ll take a look sometime in the next day or two, but will also watch this thread.

Strange. I tested the following changed code on iPhone SE, not an iPad, but still running iOS 12. I do believe with the code you had it did appear to not display with the top button (without scheduleOnce), but would display correct with bottom button (with scheduleOnce). However, I added and changed the test a bit to better display what’s going on and it appears to work fine with the following…???

I’m going to re-test your original code and do some further investigation.

void HelloWorld::runTest()
{
    static int i = 0;
    i = i + 1;
    i = i % 20;

    string filename = "test.png";
    auto renderTexture = RenderTexture::create(300, 240, Texture2D::PixelFormat::RGBA8888);
    renderTexture->beginWithClear(.5f, 0, .7f, 1);

    {
        float x = (i+1) * 10.f;
        float y = 0;
        auto sprite = Sprite::create("res/0.png");
        //auto sprite = Sprite::create("HelloWorld.png");
        auto size = renderTexture->getContentSize();
        sprite->setPosition(Vec2(40 + x, 40 + y));
        sprite->setColor(Color3B::RED);
        sprite->visit();
    }
    {
        float x = 0;
        float y = (i+1) * 5.f;
        //auto sprite = Sprite::create("HelloWorld.png");
        auto sprite = Sprite::create("res/0.png");
        sprite->setPosition(Vec2(40 + x, 40 + y));
        sprite->setColor(Color3B::GREEN);
        sprite->visit();
    }
    {
        float x = (i+1) * 10.f;
        float y = (i+1) * 5.f;
        auto sprite = Sprite::create("res/0.png");
        //auto sprite = Sprite::create("HelloWorld.png");
        auto size = renderTexture->getContentSize();
        sprite->setPosition(Vec2(40 + x, 40 + y));
        sprite->setColor(Color3B::BLUE);
        sprite->visit();
    }

    renderTexture->end();
    renderTexture->saveToFile(filename, Image::Format::PNG, true, [](RenderTexture* rt, const std::string& str) {
        CCLOG("str = %s", str.data());
    });
    Director::getInstance()->getRenderer()->render();

    string testPath = FileUtils::getInstance()->getWritablePath() + filename;
    auto texCache = Director::getInstance()->getTextureCache();
    texCache->removeTextureForKey(testPath); //remove old texture from cache

    static float offset = 10.f;
    offset += 10.f;
    offset = fmodf(offset, 200);

    auto check = Sprite::create(testPath);
    check->setTag(101); //use a tag
    check->setPosition(Vec2(400 + offset, 200));
    removeChildByTag(101); //remove old sprite before adding a new one
    addChild(check);
}
sprite->setPosition(Vec2(40 + x, 40 + y));

This line matters. Replace 40 + x with just x and you’ll see.

Yeah, apparently. Strange. I’ll take a final brief look at it tomorrow and if we don’t find the root cause then should be moved to github issue.

Thanks, altought I’ve found a workaround (scheduleOnce) I’m very curious what’s the issue. I bet on sprite batching.

1 Like

Have you found what’s the issue or should I report a bug on github?

Sorry, didn’t get back to this until now. I’ll report anything tomorrow, otherwise yeah report a bug.

Weirdly enough now I can’t reproduce the issue that we’ve discussed with the same project that I’m pretty sure I noticed the issue at least once or twice. **sigh**

Anyway, feel free to make an issue.

Just spent hours trying to figure out what I was doing wrong, because the exact same issue was happening to me.

The top 800 or so pixels of the RenderTexture (2048x2048) would be blank, yet anything below that rendered all sprites perfectly. This was happening on a button event, even while calling Director::getInstance()->getRenderer()->render() as suggested in the code comments.

I did what @piotrros did, scheduled the creation of the RenderTexture with a scheduleOnce(), and it worked, so this work-around is good enough for now.

Was a github issue ever created for this?

2 Likes

Do you have a test case I can play with? I can start an issue if one hasn’t been created.

I’ll create one, and test it out with Cocos2d-x v3.17.2 as well before posting it up.

Here are the outputs from the test project:
Correct output using scheduleOnce():

Incorrect output from a button event handler:

1 Like

@slackmoehrle The project is here:

RenderTexture test

The test was done on Windows, a win32 build using VS2019. It uses Cocos2d-x v3.17.2.

Thank you for creating this.