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();
1개의 좋아요

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

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

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

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

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

1개의 좋아요

안녕하세요.
해상도는 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);
     
 }
1개의 좋아요

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:
혹시 모르니 라즈베리에서도 한번 확인 부탁드립니다.

4개의 좋아요

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

1개의 좋아요

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

2개의 좋아요

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

1개의 좋아요

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

1개의 좋아요

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

1개의 좋아요

혹시 raspberry pi 에서 Avalonia 프로젝트를 어떻게 빌드하셨는지 알 수 있을까요?

저도 현재 raspberry pi에서 canvas 에 그려지는 것을 기능으로 하는 프로그램을 작성하고 있는데,
저는 빌드해도 SkiaSharp.so 가 gpu enable옵션이 활성화 되지 않는지 GPU의 사용률에 변화가 없고 CPU만 점유율이 늘어나네요.
GPU가속이 기본으로 설정 되어 있다고 하셨는데
혹시 설정이나 빌드를 어떻게 하셨는지 여쭙고 싶습니다.

1개의 좋아요

안녕하세요. 저도 여기저기 알아보고 옵션을 적용해봤지만 뚜렷한 해결책을 찾은건 아닙니다. 이미 검색을 통해서 아실지 모르겠지만 라즈베리파이 자체의 디스플레이 옵션이 원인이지 싶습니다. 이 때문에 아발로니아 빌드옵션을 제아무리 수정을 해도 Gpu 사용률이 떨어지지 않는것 같습니다. 결국 아발로니아 구동시 라즈베리파이 자체의 Xwayland를 이용하지 않도록 해야하는데 gpt에 나온 방식대로 해도 안되어 디스플레이 서버 구동 방식을 x11로 바꿨습니다.
x11로 바꿔서 실행시 작업관리자에서 보면 gpu가 오히려 증가하는데 버그인지 어쩐지 Gpu usage나온 사용량과 세부항목의 사용량 합계가 달라서 더 이상 알아보지 않고 그대로 두었습니다 ㅎ

1개의 좋아요

답변 감사합니다.

찾아보니 Avalonia UI 가 GPU acceleration 을 기본적으로 OpenGL 을 사용하기 때문에, 따로 설정을 안 해도 raspberry pi cm5 에서는 바로 GPU 를 사용하도록 되어있네요.

작업관리자에서 봤을 때에는 프로그램 자체의 GPU 뿐만 아니라 Wayland 에도 점유율이 영향을 주는 것으로 보아 display manager 또한 연관이 있어보이네요.

1개의 좋아요

네 리눅스 설정도 건들어야 하고 연관된 정보나 제시해준 해결책들은 제대로 동작을 안하거나 원하는 결과는 안나오고 ㅎㅎ 여튼 핵심은 Xwayland가 아닌 wayland를 네이티브?로 이용하면 된다라는것 까지 이해를 했는데… 방법을 찾으시면 공유부탁드립니다 ^^

1개의 좋아요