Avalonia Gpu 사용문제

안녕하세요. 라즈베리파이5에서 구동될 프로그램을 아발로니아를 이용하여 만들었습니다. 프로그램은 Test시 큰 문제 없이 구동이 되었지만 애니메이션이 일어날때마다 Gpu가 80%까지 치솟는데 이것저것 찾아보니 그래픽 가속이 기본옵션이라 그렇다해서 수정을 했습니다.
인터넷에서 나온 방법들과 gpt의 도움을 받아 수정을 해 70%까지는 떨쳤지만 Gpu 사용률을 30% 이내로 줄일 수 있는 방법이 더 방법이 있을까요??

  public static void Main(string[] args)
  {

      Environment.SetEnvironmentVariable("SKIA_DISABLE_GPU", "1");
      BuildAvaloniaApp()
          .StartWithClassicDesktopLifetime(args);
  }

  // Avalonia configuration, don't remove; also used by visual designer.
  public static AppBuilder BuildAvaloniaApp()
      => AppBuilder.Configure<App>()
          .UsePlatformDetect()
         // .WithInterFont()
           .With(new SkiaOptions 
           {
               // GPU 사용하지 않도록 최대한 유도
               MaxGpuResourceSizeBytes = 0
               
               
           })
      /*
          .With(new AvaloniaNativePlatformOptions
          {
              RenderingMode = new List<AvaloniaNativeRenderingMode>
              {
                  AvaloniaNativeRenderingMode.Software
              }
          })
      */
          .LogToTrace()
          .UseSkia()
          
          .UseReactiveUI();

GPU 사용률을 제한하는 특별한 이유가 있으신가요?

Environment.SetEnvironmentVariable("SKIA_DISABLE_GPU", "1");
...
MaxGpuResourceSizeBytes = 0

위와 같은 방식으로 GPU가 하는 일을 CPU가 하면 부하가 훨씬 더 걸립니다.

애니메이션 로직을 최적화 하는 방향으로 개선이 필요해 보입니다.

실행 환경의 디스플레이 해상도와 구현하신 애니메이션 관련된 코드도 첨부해주시면 답변에 도움이 될 듯합니다.

안녕하세요.
해상도는 1920 X 1080 입니다.

Gpu를 최대한 낮게 가져가려는 이유가 팀장님께서 라즈베리에 AI의 일부기능을 올릴지도 모르니 Gpu를 최대한 줄여보라고 하시고 단순 제품이동 대쉬보드 프로그램인데 Gpu를 높게 쓰는게 이상하다 합니다.
가만 생각해보니 요구사항을 떠나서 단순한 객체 이동 애니메이션인데 Gpu를 사용하것도 개인적으로 궁금하기도 합니다. (팀장님께서는 이동에 따른 화면을 다시 그리는 것 아니냐고 의심합니다.)

객체에 애니메이션을 걸어 캔버스에 자식으로 넣어 Left 값을 늘려서 이동을 구현 하고 애니메이션 각 포인트마다 총 3개로 구성되있습니다.
(캔버스에 생성된 객체를 자식으로 추가는 mainWindow 비하인드코드에 기술되어 있습니다.)
객체가 늘어나면 애니메이션이 실행 수가 늘어나니 Gpu가 올라갈거라 예상했지만 객체가 1개만 있어도 80%를 찍네요 ㅎㅎ;;

아래는 주황색 제품 객체의 애니메이션 관련 코드입니다.

     ....중 략....

            animation1 = new Animation
            {
                Duration = TimeSpan.FromSeconds(3),
                IterationCount = new IterationCount(1),
                  FillMode = FillMode.Forward,
                Children =
                {
                    new KeyFrame
                    {
                        Cue = new Cue(0.0),
                        Setters = 
                        {
                            new Setter(Canvas.LeftProperty, Convert.ToDouble(0))
                        
                        }
                    },
                     new KeyFrame
                    {
                        Cue = new Cue(1),
                        Setters =
                        {
                            new Setter(Canvas.LeftProperty , Convert.ToDouble(240))

                        }
                    },                
                }
            };

            animation2 = new Animation
            {
                Duration = TimeSpan.FromSeconds(5),
                IterationCount = new IterationCount(1),
                //PlaybackDirection = PlaybackDirection.Normal,
                FillMode = FillMode.Forward,
                Children =
                {
                    new KeyFrame
                    {
                        Cue = new Cue(0.0),
                        Setters =
                        {
                            new Setter(Canvas.LeftProperty, Convert.ToDouble(240))
                        }
                    },
                     new KeyFrame
                    {
                        Cue = new Cue(1.0),
                        Setters =
                        {
                            new Setter(Canvas.LeftProperty , Convert.ToDouble(665))
                        }
                    }
                }
            };
            animation3 = new Animation
            {
                    Duration = TimeSpan.FromSeconds(1.8),
                    IterationCount = new IterationCount(1),
                    //PlaybackDirection = PlaybackDirection.Normal,
                    FillMode = FillMode.Forward,
                    Children =
                    {
                        new KeyFrame
                        {
                            Cue = new Cue(0.0),
                            Setters =
                            {
                                new Setter(Canvas.LeftProperty, Convert.ToDouble(665))
                            }
                        },
                         new KeyFrame
                        {
                            Cue = new Cue(1.0),
                            Setters =
                            {
                                new Setter(Canvas.LeftProperty , Convert.ToDouble(826))
                            }
                        },
                    }
             };

            eClipse.Stroke = new SolidColorBrush(Colors.Black);
            eClipse.StrokeThickness = 1;
            grid.Children.Add(eClipse);           

            eClipse.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center;
      
           
            Style style = new Style(x => x.OfType<Grid>().Name("go2"));
            style.Animations.Add(animation1);
            style.Animations.Add(animation2);
            style.Animations.Add(animation3);

     ....중략.....

  public async Task decision1()
  {

      await animation1.RunAsync(grid , cancellationTokenSource.Token);
       await decision2();
 
    }

  public async Task decision2() 
  {
     
       await Task.Delay(500);
       await animation2.RunAsync(grid, cancellationTokenSource.Token);
       await decisionFinal();  
  }

  public async Task decisionFinal() 
  {
      await Task.Delay(500);
      await animation3.RunAsync(grid);
       await Task.Delay(500);
      this.Dispose();
  
  }

 public void Dispose()
 {
     ((Canvas)grid.Parent).Children.Remove(grid);
     
 }

Canvas.Left 속성에 Animation을 적용할 경우 Layout 업데이트가 발생할 것이고, 레이아웃 업데이트가 발생하면 업데이트에 영향받은 Visual을 다시 그리는 렌더링 업데이트가 발생할 것입니다. 즉 CPU + GPU 부하가 발생하는 상황인듯 한데요,

Canvas.Left 속성 대신 각 객체의 RenderTransform 속성에 TranslateTransform을 지정하시고 TranslateTransform.X 속성을 업데이트 해보시기 바랍니다.

<Grid Name="go2" ...>
    <Grid.RenderTransform>
        <TranslateTransform/>
    </Grid.RenderTransform>
</Grid>

테스트 해보지 않았지만 아마 아래처럼 수정하면 될듯합니다.

    ...
     new KeyFrame
     {
         Cue = new Cue(0.0),
         Setters =
         {
             new Setter(TranslateTransform.XProperty, Convert.ToDouble(665))
         }
     },

이렇게하면 Visual을 다시 그리지 않고 캐싱된 Visual에 대해 최소한의 렌더링만 적용되어 GPU 사용량이 낮아질 듯 합니다.

기존의 부하가[레이아웃:CPU] 10 + [요소 렌더링:GPU] 10 + [레이아웃 렌더링:GPU] 1 이었다면 RenderTransform[레이아웃 렌더링:GPU] 1만 처리하는 방식입니다.

(GPU 관련 속성을 Disable 한 부분을 모두 해제해 보세요. 그래야 부하가 더 적습니다.)


※ Windows에서 테스트 결과 RenderTransform 방식이 GPU를 더 많이 사용하는 것을 확인했습니다. WPF와는 구현이 다른듯 합니다.:joy::joy:
혹시 모르니 라즈베리에서도 한번 확인 부탁드립니다.

3개의 좋아요

그것이 라즈베리파이니까요
20만원짜리 PC에서 로스트아크 돌리는 격

저도 내심 그말이 너무하고 싶습니다. 아무리 성능이 좋아졌더라도 소형 단일보드PC의 한계라고 ㅋㅋㅋ Gpu 성능을 떨칠게 아니라 구동되는 프로그램을 제한해야 한다고 말하고 싶네요. Ai를 돌리고 DB , 통신 , 각종 센서까지 ㅎㅎ

1개의 좋아요

네 소스수정해서 Test 해보고 결과 공유해드리겠습니다. 너무 감사드립니다~!

Raspberry Pi 5는 4K60 프레임 비디오도 끊김 없이 재생할 수 있는 하드웨어 성능을 가지고 있습니다.
AI까지는 아니더라도 “DB , 통신 , 각종 센서” 정도는 동시에 구동하고도 남아돌죠ㅎ

안녕하세요. transform으로 이동처리를 해밨지만 여전히 gpu 사용률이 높습니다 ㅠㅠ 좀더 다른 방법을 찾아봐야 겠습니다만 그냥 애니메이션을 없애고 Task를 써서 속성값을 변경해볼까 합니다.