-
-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Android - Wrong FPS/update on high rate devices #2129
Comments
I did some digging around, and found another way to sync the drawing. I think this would be a better solution than sleeping or triggering rendering from another thread, since it gives more precise control. So this method would have 2 parts. First part is getting a callback when vsync happens, since this is the ideal time we want to start the frame update and rendering. There's Choreographer.FrameCallback that does this. In this callback we also get the time when vsync has started. Here we could check if we should render a frame now (in case app requests less FPS than actual refresh rate), and then request the manual frame render from The second part is to tell when we want to display our rendered frame. For this there's eglPresentationTimeANDROID(), that takes the time when the frame should be displayed. We would need to pass N+2 frame time. The only problem might be if it's impossible to get those other parameters that need to be passed in. The advantage this method gives is that updates happen on vsync, and we control how many frames are in flight, thus minimizing the latency by not stuffing frame queue full. Related resources: |
I don't think it is necessary to go this low level, at least the proposed solution works, as opposed to the live one which doesn't. |
@smilediver I had a look at the solution you gave, and it’s a bit more complicated (in the sense you gave on the PR). What seems to be better is that the ui synchronization is right on the thread whereas my PR could occur slightly before or before. This is a good thing but what I’m wondering about is the parameters you talked about (maybe we could find them) and overall the « frame pattern » we should use: if we were to use a multiple of the device refresh rate we wouldn’t have issue, but android device refresh rate could be any from 1/60 to 1/120 or even more, so you end up computing the frames like in the example java file (32232 etc) and I’m not sure it would work well 🤔 |
I think it would be less complicated. Maybe you think so because of the Grafika example, but I added that Grafika link just as a reference we can use to get some ideas or how to implement certain things. We certainly don't need majority of stuff in there. The vsync callback needs only a check if something should be rendered on this vsync or not, which is always true, except if user wants less FPS than the device refresh rate, and then request the rendering if needed. And after the rendering it's just a call to give the time when frame is supposed to be on screen. So it should be pretty easy to understand what's happening. I was also thinking what should happen if user requests less FPS than device refresh rate. I think patterns like It seems the parameters for Btw, in your PR, I think, render start time can drift in the whole interval from one vsync to another, since it's not syncing, and |
It’s sure once we get rid of the useless stuff it should be more understandable
That’s definitely the point I have doubt about. The fact is that the cocos interval should better be set as an enum (Max, 60,30,20,15) and not as a float so it would be sure your program runs at the desired frequency, but it would require every supported platform to be modified/adapted. In the case the user wants 40fps and we do 30fps ofc it would run graphically but what if the user wants to compute something using
Great
yeah |
@smilediver I tried to use the In the meantime I saw that there was other ways to "set" a frame rate (not always working as wanted) and lately since Android 11 there is even a As side note, I tried to use setFrameRate(60, DEFAULT) on my Pixel 6 and it effectively run at 60FPS; I tried with 30 too but it runs at 60FPS anyway... They also explain that using Choregrapher itself is a simple but not full solution, and since then a thing called AGDK is made to handle the rate, available since Android Api level 19 / NDK 18. |
Nice find on that blog post, some good insights there.
I was aware of frame pacing module in AGDK library, but it seems like an overkill to integrate it just for running the game loop. Anyway, I'm also planning to take a look at this problem later this week, maybe I can get something working. Edit: this is probably the reason why calls to |
Yeah I agree it is too recent, and not working as we want (like for 30 FPS) so not a solution
That's what I thought too, but I also saw Axmol support starts from Android level api 17 not 19, if I'm correct
Great
Good point, but the thing is we have EGL10 in Axmol, mixing to have both seems a bit tricky and I'm not sure we can change EGL10 to EGL14 that easily |
update method relies on delta time between two frames. If this time is true, do not think that it is good idea to link this method to draw fps. |
I've implemented this loop using |
@AlexandreK38 There was an issue introduced in #1504, which resulted in incorrect update calls. The attempt to fix this is in #2162. Would this be the source of the issue you're experiencing too? |
@rh101 Yeah this is the same issue. The change made in the #2162 is basically the "simple" solution I put above here :) |
OK! That's good, since now we know this will work. For the time being, #2162 will most likely be used as the interim solution until a decision is made on implementing one of the more drastic changes to the Android rendering code, such as in your PR and the one from @smilediver. |
Agreed, #2162 should be merged, until we decide on another solution. |
Simple solution was merged into dev branch, closing the issue |
Environment:
Issue:
Hi,
I use
scheduleUpdate()
so that my code is updated on each frame drawn. If I set the frame interval to 1/60s I expect theupdate()
to be called once every 1/60s, but on my Pixel 6 it will be called twice.So the issue occurs on high rate devices (120FPS) like the Pixel 6.
The code itself tells they are not handled:
A possible fix to this is this following code:
It works, but in any case (current and possible fix) the UI rendering seems a bit laggy because the renderer is using
RENDERMODE_CONTINUOUSLY
mode meaning it's the java thread that triggers the call periodically and sleeping in theonDrawFrame()
might cause troubles to it.I explored this solution and a new one using our own thread to trigger manually the rendering (see PR #2125) and the thread solution was giving better performance, overall when you do much work at each frame.
The text was updated successfully, but these errors were encountered: