Skiasharp: [Android] Uncapped framerates (300fps) on SKGLView.PaintSurface after updating package

Created on 27 May 2018  路  9Comments  路  Source: mono/SkiaSharp

Description

Application framerate went from a capped 60fps to a ~300fps.

Upgrading to SkiaSharp.Views.Forms version="1.60.1" from version="1.59.3"
Using SKGLView with HasRenderLoop to true,

Expected behavior

Like in version="1.59.3" framerate should not go higher than 60fps

Actual behavior

Framerate is not fixed, going from 60fps to 1000fps.
It modifies all the animations of the application that are frame based.

Basic Information

Framerate is based on the elapsedTime between each PaintSurface event.

  • Version with issue: 1.60.1
  • Last known good version: 1.59.3
  • Xamarin Version : Xamarin.Forms version="3.0.0.446417"
  • IDE: Visual Studio Community 2017
  • Platform Target Frameworks:

    • Android: 7 API 24

  • Target Devices:

    • Samsung galaxy S7 Edge

area-SkiaSharp.Views backend-OpenGL os-Android status-help-wanted type-bug

Most helpful comment

Also, with regards to the frame rate... It is never good to use a "constant" frame rate - either an assumption or using some limiter API.
There is never a guarantee because another app may suddenly use the CPU/GPU, or particular logic in a particular frame may take longer to run, or even the time it takes to swap buffers may change between frames due to reasons.

All 9 comments

This never was a feature. Maybe they changed something in Forms... Did you update to a newer forms version?

SkiaSharp will always render as fast as possible, with as many frames as possible.

Did you maybe change devices, OS versions or platform?

I will have a look, but I don't think anything significant has changed.

I _see_ the change, and I think it has to do with the fact that we are no longer using the old GLSurfaceView to render the content, but the new TextureView.

Unfortunately, I can't seem to find where the original code limits the framerate. Also, I am not sure that we _want_ to limit the frame rate.

I will look further and see what I can do.

Ok, I see

I use Skiasharp as a game renderer. And the physical Engine of the game is based on the frame rate, which is wrong !
It should have a separated refresh rate based on time.

I knew I had to make this change anytime soon. Now is just the perfect timing to do it =)

I was curious about what produced this change. Thank you for the hints of answers. The current app impacted by this change makes a massive use of SKCanvas.DrawBitmap(...). It does mean that the perfomances of SkiaSharp have increased, which is very good !

As it is not really an issue, should I close now ?

Let's leave it open, and I will try and find a real reason the GLSurfaceView has a frame rate limit, but the TextureView does not.

Just for reference later on in life:

This is the code I used the measure the FPS:

Stopwatch sw = Stopwatch.StartNew();
TimeSpan last;

private void OnPaint(object sender, SKPaintGLSurfaceEventArgs e)
{
    var c = sw.Elapsed;
    var ts = c - last;
    last = c;

    var fps = 1.0 / (ts.TotalSeconds);
    var fpsString = fps.ToString("0,000.00");

    Log.Debug("FPS", fpsString);
}

Also, with regards to the frame rate... It is never good to use a "constant" frame rate - either an assumption or using some limiter API.
There is never a guarantee because another app may suddenly use the CPU/GPU, or particular logic in a particular frame may take longer to run, or even the time it takes to swap buffers may change between frames due to reasons.

I really need to look at this. Noticing 1.2K FPS in some emulators.

A simple FPS snippet: https://gist.github.com/mattleibow/4eb9cd26bcaaaefd5b2f7499d21bf4bd

I mean, isn't that good to have the FPS uncapped. As a user of SkiaSharp for backend game renderer as well, I find it quiet good improvement.
Also if you are looking for the cause of capped vs uncapped FPS, then the explanation could be simple: GLSurfaceView probably uses V-Sync (*glSwapInterval call), when TextureView doesn't use that.

Having uncapped is nice, but a heavy drain. Also, it is no point having the updates faster than the screen - that wastes resources.

In the case of "capping" it is usually with regards to the screen refresh rate.

We might be able to look at Choreographer: https://stackoverflow.com/questions/55028881/how-to-determine-device-screen-refresh-start-stop-on-mobile-devices

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Choreographer;

private class DisplayFrameSync extends Thread implements Choreographer.FrameCallback {
    private volatile Handler signal;
    private Consumer<Long> update;

    public DisplayFrameSync(Consumer<Long> update) {
        super();

        this.update = update;
    }

    @Override
    public void run() {
        setName("DisplayFrameSyncThread");

        Looper.prepare();

        signal = new Handler() {
            public void handleMessage(Message msg) {
                Looper.myLooper().quit();
            }
        };

        Choreographer.getInstance().postFrameCallback(this);

        Looper.loop();
        Choreographer.getInstance().removeFrameCallback(this);
    }

    public void signal() {
        if (this.signal != null) {
            this.signal.sendEmptyMessage(0);
            this.signal = null;
        }
    }

    @Override
    public void doFrame(long timeNano) {
        if (this.update != null) {
            this.update.accept(timeNano);
        }

        Choreographer.getInstance().postFrameCallback(this);
    }
}
Was this page helpful?
0 / 5 - 0 ratings