首页 > 解决方案 > 在 Xamarin 中使用 CocoSharp 绘制移动图表会导致帧速率变慢

问题描述

我正在制作一个显示加速度计折线图的应用程序。图表必须横向移动以显示新结果。绘制我使用 CocoSharp 图形库。

第一次尝试是每次更新都重新绘制完整的图表,但这不够快,而且你不能以某种方式绘制透明,这不会擦除以前绘制的像素,所以我的图表刚刚填满。

第二次尝试是让图表比屏幕宽 4 倍,并且只在 2 个位置绘制最后一次更新:在我们正在绘制的位置和 2 个屏幕宽度后面(屏幕外)。在画线之前,我画了一条黑线而不是透明线来擦除以前的结果。

然后我将整个图层向左移动,给出一个漂亮的滚动图表。然后当位置超过 2 个屏幕宽度时,我将位置重置回 0,但是因为我们在 2 个位置绘制图形已经绘制与我们离开的位置相同,使其永远无缝滚动。

这效果很好,但在绘制几分钟后,您会注意到屏幕开始出现帧率滞后。我调查了一切,但我的绘图线程工作得很好,每 16 毫秒抽出一次完整的更新,所以我认为它是 CocoSharp 库。

正如我之前所说,当您绘制透明时,像素不会被擦除,这让我认为每次绘制更新​​都会以某种方式保存在内存中,一段时间后会减慢 gpu 的速度。这可能是正确的吗?如果是这样,我该如何解决?

using CocosSharp;
using JumpMeter.Shared;
using JumpMeter.Shared.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JumpMeter
{
    public class ChartLayer
    {
        public ChartLayer(CCScene scene, float x, float y, float width, float height)
        {
            List = new ConcurrentQueue<CalculationLog>();

            RasterLayer = new CCLayer();
            RasterNode = new CCDrawNode();

            GraphLayer = new CCLayer();
            GraphNode = new CCDrawNode();

            // Add the background layer
            RasterLayer.AddChild(RasterNode);

            // Draw the "zero lines"
            RasterNode.DrawLine(new CCPoint(0, height / 2), new CCPoint(width, height / 2), 0.1f, CCColor4B.White);
            RasterNode.DrawLine(new CCPoint(0, 2), new CCPoint(width, 2), 0.1f, CCColor4B.White);
            scene.AddLayer(RasterLayer);

            // Add the graphics layer
            GraphLayer.AddChild(GraphNode);
            scene.AddLayer(GraphLayer);

            Width = width;
            Height = height;

            RasterLayer.PositionX = x;

            RasterLayer.PositionY = y;
            GraphLayer.PositionY = y;
        }

        public CCLayer RasterLayer { get; }
        public CCDrawNode RasterNode { get; }

        public CCLayer GraphLayer { get; }
        public CCDrawNode GraphNode { get; }

        float Width { get; }
        float Height { get; }

        // Buffer for the drawn items
        ConcurrentQueue<CalculationLog> List { get; }

        // Current position of the graph
        float Position { get; set; }

        // Here we get the new values for the graph
        public void AddValue(CalculationLog value)
        {
            List.Enqueue(value);
        }

        // This function will be called every 16ms
        public bool Draw()
        {
            while (List.TryDequeue(out CalculationLog value))
            {
                Position += Constants.GraphSpeed; // = 0.1f

                // If the position is bigger than 2 screen widths (* 1000 for ultra slow speeds)
                if (Convert.ToInt32(Position * 1000) >= Convert.ToInt32(Width * 2 * 1000))
                    Position = 0;

                // Calculate real positions
                var x = Position + RasterLayer.PositionX;
                var y = Convert.ToSingle(value.AccelerationUp / Constants.Scale) * (Height / 2) + Height / 2;

                if (y > Height) y = Height;
                if (y < 0) y = 0;

                var y2 = Convert.ToSingle(value.Height / Constants.Scale * 2) * (Height / 2) + 1;
                if (y2 > Height) y2 = Height;
                if (y2 < 0) y2 = 0;

                // Then draw them
                GraphNode.DrawLine(new CCPoint(x, 0), new CCPoint(x, Height), Constants.GraphSpeed, CCColor4B.Black);
                GraphNode.DrawLine(new CCPoint(x, Height / 2), new CCPoint(x, y), Constants.GraphSpeed, CCColor4B.White);
                GraphNode.DrawLine(new CCPoint(x, y2), new CCPoint(x, y2 + 1), Constants.GraphSpeed, CCColor4B.White);

                GraphNode.DrawLine(new CCPoint(x - Width * 2, 0), new CCPoint(x - Width * 2, Height), Constants.GraphSpeed, CCColor4B.Black);
                GraphNode.DrawLine(new CCPoint(x - Width * 2, Height / 2), new CCPoint(x - Width * 2, y), Constants.GraphSpeed, CCColor4B.White);
                GraphNode.DrawLine(new CCPoint(x - Width * 2, y2), new CCPoint(x - Width * 2, y2 + 1), Constants.GraphSpeed, CCColor4B.White);
            }

            // Move the position of the graph layer
            GraphLayer.PositionX = Convert.ToInt32(Width - Position);

            return true;
        }
    }
}

(我实际上是在 1 个图中绘制 2 个图,我在一般描述中忘记了这一点)

标签: c#androidxamarin.forms

解决方案


好吧,看来我是对的。在没有绘制图表的情况下对其进行测试后,应用程序并没有开始口吃。我现在将图表更改为使用 2 层,方式与我过去所做的几乎相同,只是当我重置滚动时,我还重置了之前的 CCDrawNode :

GraphNode.Clear();

完整代码:

public class ChartLayer
{
    public ChartLayer(CCScene scene, float x, float y, float width, float height)
    {
        List = new ConcurrentQueue<ICalculationLog>();

        RasterLayer = new CCLayer();
        RasterNode = new CCDrawNode();

        GraphLayer1 = new CCLayer();
        GraphNode1 = new CCDrawNode();

        GraphLayer2 = new CCLayer();
        GraphNode2 = new CCDrawNode();

        // Add the background layer
        RasterLayer.AddChild(RasterNode);

        // Draw the "zero lines"
        RasterNode.DrawLine(new CCPoint(0, height / 2), new CCPoint(width, height / 2), 0.1f, CCColor4B.White);
        RasterNode.DrawLine(new CCPoint(0, 2), new CCPoint(width, 2), 0.1f, CCColor4B.White);
        scene.AddLayer(RasterLayer);

        // Add the graphics layer
        GraphLayer1.AddChild(GraphNode1);
        scene.AddLayer(GraphLayer1);

        GraphLayer2.AddChild(GraphNode2);
        scene.AddLayer(GraphLayer2);

        Width = width;
        Height = height;

        RasterLayer.PositionX = x;

        RasterLayer.PositionY = y;
        GraphLayer1.PositionY = y;
        GraphLayer2.PositionY = y;
    }

    public CCLayer RasterLayer { get; }
    public CCDrawNode RasterNode { get; }

    bool Node1Selected = false;


    public CCLayer GraphLayerForeground => Node1Selected ? GraphLayer1 : GraphLayer2;
    public CCDrawNode GraphNodeForeground => Node1Selected ? GraphNode1 : GraphNode2;
    public CCLayer GraphLayerBackground => Node1Selected ? GraphLayer2 : GraphLayer1;
    public CCDrawNode GraphNodeBackground => Node1Selected ? GraphNode2 : GraphNode1;

    public CCLayer GraphLayer1 { get; }
    public CCDrawNode GraphNode1 { get; }
    public CCLayer GraphLayer2 { get; }
    public CCDrawNode GraphNode2 { get; }

    float Width { get; }
    float Height { get; }

    // Buffer for the drawn items
    ConcurrentQueue<ICalculationLog> List { get; }

    // Current position of the graph
    float Position { get; set; }

    // Here we get the new values for the graph
    public void AddValue(ICalculationLog value)
    {
        List.Enqueue(value);
    }

    // This function will be called every 16ms
    public void Draw()
    {
        while (List.TryDequeue(out ICalculationLog value))
        {
            Position += Constants.GraphSpeed; // = 0.1f

            // If the position is bigger than 2 screen widths (* 1000 for ultra slow speeds)
            if (Convert.ToInt32(Position * 1000) >= Convert.ToInt32(Width * 2 * 1000))
            {
                Position = 0;
                Node1Selected = !Node1Selected;
                GraphNodeBackground.Clear();
            }

            // Calculate real positions
            var x = Position + RasterLayer.PositionX;
            var y = Convert.ToSingle((value.Acceleration.Z - 1) / Constants.Scale) * (Height / 2) + Height / 2;

            if (y > Height) y = Height;
            if (y < 0) y = 0;

            var y2 = Convert.ToSingle(value.Height / Constants.Scale * 2) * (Height / 2) + 1;
            if (y2 > Height) y2 = Height;
            if (y2 < 0) y2 = 0;

            // Then draw them
            //GraphNode1.DrawLine(new CCPoint(x, 0), new CCPoint(x, Height), Constants.GraphSpeed, CCColor4B.Black);
            GraphNodeForeground.DrawLine(new CCPoint(x, Height / 2), new CCPoint(x, y), Constants.GraphSpeed, CCColor4B.White);
            GraphNodeForeground.DrawLine(new CCPoint(x, y2), new CCPoint(x, y2 + 1), Constants.GraphSpeed, CCColor4B.White);

            //GraphNode1.DrawLine(new CCPoint(x - Width * 2, 0), new CCPoint(x - Width * 2, Height), Constants.GraphSpeed, CCColor4B.Black);
            GraphNodeBackground.DrawLine(new CCPoint(x - Width * 2, Height / 2), new CCPoint(x - Width * 2, y), Constants.GraphSpeed, CCColor4B.White);
            GraphNodeBackground.DrawLine(new CCPoint(x - Width * 2, y2), new CCPoint(x - Width * 2, y2 + 1), Constants.GraphSpeed, CCColor4B.White);
        }

        // Move the position of the graph layer
        GraphNodeForeground.PositionX = Convert.ToInt32(Width - Position);
    }
    public void Clear()
    {
        GraphNode1.Clear();
    }
}

推荐阅读