Hey all. I’m creating a pixel art game (Songbringer) with a screen res of 420x240 that gets scaled up to whatever the window size is (example 2560x1800). I want to reduce the workload of the GPU and I also want the game to look more like a true pixel art game. Is there a way to get cocos2d-x to render everything to a framebuffer that is only 420x240, then scale it up to the window size after rendering?
See, here’s how the shadows look in the game now (they use a custom blur shader which you can tell is running at the higher resolution):
And here is how I want them to look:
I’ve already tried the obvious stuff like:
Changing the size of the window or the frame zoom factor with GLViewImpl::createWithRect()
Changing the design resolution or design res policy
That’s on my todo list as well mostly for WinPhone/WinRT since it renders to a smaller frame buffer (roughly 1/4 resolution), but then is scaled up with the hardware scalar ( https://github.com/MSOpenTech/angle/issues/42 ).
I’d probably start with the CameraFrameBuffer test. If you figure it out let me know.
My only concern would be touch/mouse event locations for UI and gameplay driven by touch (pan, select, etc). With game controllers and keyboard you’ll be fine. Maybe the touch virtual d-pad (and similar) will be solved by rendering those to a separate camera.
After a quick test using code from CameraFramebufferTest in a new project I found that it should work for you, but performance testing would need to confirm there’s any benefit (there should be a large benefit ideally due to much reduced pixel count, except for the final render of the sprite with the frame buffer texture).
I needed to set design resolution to same as frame buffer or it won’t be pixel perfect since it will texture filter out pixels or stretch them due to UV mapping.
glview->setDesignResolutionSize(420, 240, ResolutionPolicy::SHOW_ALL);
auto fboSize = iSize(420,240);
auto fbo = cocos2d::experimental::FrameBuffer::create(1, fboSize.w, fboSize.h);
also in a quick test I needed to flip the sprite used for rendering the frame buffer to screen
auto sprite = Sprite::createWithTexture(fbo->getRenderTarget()->getTexture());
sprite->setFlippedY(true);
Thanks, @stevetranby !! Yes, it totally works and because the game uses custom shaders the performance boost is significant. Instead of running a fragment shader for 4.6 million pixels, it does for only 100 thousand.
Big win! Especially for the GPU.
Today’s live stream and Youtube vid will cover the resultant code. I basically just copied CameraFramebufferTest including the RenderTarget and RenderTargetDepthStencil. I wonder if either of those are unnecessary.
I’m guessing touch positions will be fine as with this technique you technically have both full screen and fbo elements. One simply selects which camera to render to using Node::setCameraMask.
Will have to test on Windows too (and Linux eventually).
@natweiss Hey could you link me to the vid with your discussion of getting this working, I’m doing something very similar with shadow mapping so it would be great to get some tips.
I noticed you can share the fbo, rt and even rtTexture throughout different scenes. Only the sprite and camera nodes have to be unique to make the sceneGraph happy.
@stevetranby What to do, if I want to zoom in, to have FrameBuffer sprite fullscreen?
My designResolution is 960x720, the size if the framebuffer is 320x240 (for shader reasons). After that I want to show the framebuffer sprite fullscreen without doing frameBufferSprite->Scale() because I hope, I will get a better performance if it is achieved with camera or viewport? Thank you!
@natweiss Are you sure about the performance improvements? I have done the same, doing a Hqx shader on my frame buffer sprite with 320x240 but to display it afterwards fullscreen, I have to scale the frame buffer sprite and my FPS are getting low -> on iPad Air about 40 FPS, without scaling of frame buffer sprite 60 FPS.
Are you doing any postprocessing effects on the scaled image? I think you should do those on the 320x240 frame buffer.
The shader rendering the scaled buffer (aka the final frame rendered) should just be texturing a quad so it’s not performance heavy.
my problem is maybe not the shader. I have a new approach, trying to render the frame buffer to a render texture and doing the required scaling on the render texture and not on the frame buffer sprite.
But the FPS are only 40 FPS. Maybe I am doing something wrong there. Could you kindly have a look? All I want to achieve is, apply shader on FBO and render it to a render texture which I will upscale to have a fullscreen display. Thank you!
Couldn’t you render the scene directly on the RenderTexture and avoid using the lFrameBuffer and lFrameBufferSprite? Then you just render the RenderTexture’s sprite and scale it as much as you want.
It seems so You could try debugging the cocos2d-x code and try to find the root cause.
I still don’t understand why you are using FrameBuffer instead of using RenderTexture. RenderTexture will handle all the frame buffer stuff for you and after rendering to the RenderTexture you can just use the RenderTexture sprite and add it to your scene.
Ok, I will give it a try. Framebuffer was really nice because my scene was rendered (resized) to 320x240 pixels instead of original resolution (960x720). What would be the best way to achieve this with use of RenderTexture?