나는 현재 동일한 문제가있을 가능성이 있으며 나는 예상치 못한 것을 발견했다. WriteableBitmap에 렌더링하고 사용자가 스크롤 (줌) 및 팬하여 렌더링 된 것을 변경하도록 허용합니다. 줌과 패닝 모두 움직임이 고르지 않아 렌더링이 너무 오래 걸리는 것으로 생각했습니다. 일부 계측 후에는 30-60 fps로 렌더링한다는 것을 확인했습니다. 사용자가 확대/축소 또는 이동하는 방법에 관계없이 렌더링 시간이 늘어나지 않으므로 초퍼성이 다른 곳에서 발생해야합니다.
대신 OnMouseMove 이벤트 처리기를 살펴 보았습니다. WriteableBitmap은 초당 30-60 번 업데이트되지만 MouseMove 이벤트는 초당 1-2 회만 발생합니다. WriteableBitmap의 크기를 줄이면 MouseMove 이벤트가 더 자주 발생하고 팬 작업이 부드럽게 나타납니다. 따라서 choppiness는 실제로 MouseMove 이벤트가 고르지 않고 렌더링되지 않습니다 (예 : WriteableBitmap이 7-10 프레임을 렌더링하고 MouseMove 이벤트가 발생하면 WriteableBitmap이 새로 이동 한 이미지의 7-10 프레임을 렌더링 함). , 등).
MouseableGitPosition (this)을 사용하여 WriteableBitmap이 업데이트 될 때마다 마우스 위치를 폴링하여 팬 작업을 추적하려고했습니다. 그러나 반환 된 마우스 위치가 새 값으로 변경되기 전에 7-10 프레임에서 동일하기 때문에 동일한 결과가 나타납니다.
나는 다음 폴링에게 PInvoke를 서비스 GetCursorPos like in this SO answer 예를 사용하여 마우스 위치 시도 :
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
를이 실제로 트릭을했다. GetCursorPos는 호출 될 때마다 (마우스가 움직일 때마다) 새 위치를 반환하므로 사용자가 패닝하는 동안 각 프레임이 약간 다른 위치에 렌더링됩니다. 같은 종류의 choppiness가 MouseWheel 이벤트에 영향을 미치는 것으로 보이며, 나는 그 이벤트를 해결하는 방법을 모릅니다.
따라서 시각적 트리를 효율적으로 유지 관리하는 것에 대한 위의 조언이 모두 우수하지만, 성능 문제는 마우스 이벤트 빈도를 방해하는 결과 일 수 있습니다. 제 경우에는 어떤 이유로 렌더링이 마우스 이벤트가 평상시보다 훨씬 느리게 업데이트되고 실행되는 것으로 보입니다. 이 부분적인 해결 방법보다는 진정한 해결책을 찾으면 이것을 업데이트하겠습니다.
편집는 : 좋아, 나는이 좀 더 파고 나는 내가 지금 무슨 일이 일어나고 있는지 이해 생각합니다. 보다 자세한 코드 샘플을 사용하여 설명하겠습니다.
this MSDN article.에 설명 된대로 CompositionTarget.Rendering 이벤트를 처리하도록 등록하여 프레임 단위로 비트 맵을 렌더링합니다. 기본적으로 UI가 렌더링 될 때마다 내 비트 맵을 업데이트 할 수 있도록 내 코드가 호출됩니다. 이것은 본질적으로 당신이하는 렌더링과 동일합니다. 단지 비주얼 엘리먼트를 어떻게 설정했는지에 따라 렌더링 코드가 뒤에서 호출된다는 것입니다. 그리고 렌더링 코드는 볼 수있는 곳입니다. OnMouseMove 이벤트를 재정 의하여 마우스의 위치에 따라 변수를 업데이트합니다.
public class MainWindow : Window
{
private System.Windows.Point _mousePos;
public Window()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
// Update my WriteableBitmap here using the _mousePos variable
}
protected override void OnMouseMove(MouseEventArgs e)
{
_mousePos = e.GetPosition(this);
base.OnMouseMove(e);
}
}
문제는 렌더링 시간이 더 걸립니다, MouseMove 이벤트 (모든 마우스 이벤트, 정말이) 훨씬 덜 빈번하게 호출되는 점이다. 렌더링 코드에 15ms가 걸리면 MouseMove 이벤트가 몇 밀리 초마다 호출됩니다. 렌더링 코드에 30ms가 걸리면 MouseMove 이벤트가 백 밀리 초마다 호출됩니다. 왜 이런 일이 일어나는가에 대한 내 이론은 WPF 마우스 시스템이 값을 업데이트하고 마우스 이벤트를 발생시키는 동일한 스레드에서 렌더링이 일어나고 있다는 것입니다. 이 스레드의 WPF 루프에는 한 조건에서 렌더링이 너무 오래 걸리면 마우스 업데이트를 건너 뛸 수있는 조건부 논리가 있어야합니다. 내 렌더링 코드가 모든 단일 프레임에서 "너무 오래"걸릴 때 문제가 발생합니다. 그런 다음 렌더링이 프레임 당 15 여분의 ms를 필요로하기 때문에 인터페이스가 약간 느려지는 대신 15 밀리 초의 렌더링 시간으로 인해 마우스 업데이트 사이에 수백 밀리 초의 지연이 생겨 인터페이스가 크게 혼란스럽게됩니다.
앞서 언급 한 PInvoke 해결 방법은 기본적으로 WPF 마우스 입력 시스템을 우회합니다. 렌더링이 발생할 때마다 소스로 곧바로 이동하므로 WPF 마우스 입력 시스템을 굶주리게해도 더 이상 비트 맵이 올바르게 업데이트되지 않습니다.
public class MainWindow : Window
{
private System.Windows.Point _mousePos;
public Window()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
POINT screenSpacePoint;
GetCursorPos(out screenSpacePoint);
// note that screenSpacePoint is in screen-space pixel coordinates,
// not the same WPF Units you get from the MouseMove event.
// You may want to convert to WPF units when using GetCursorPos.
_mousePos = new System.Windows.Point(screenSpacePoint.X,
screenSpacePoint.Y);
// Update my WriteableBitmap here using the _mousePos variable
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
}
이 방법은 그러나, 내 마우스 이벤트 (MouseDown,을 MouseWheel 등)의 나머지가 해결되지 않았고, 내 마우스 입력의 모든이의 PInvoke 접근 방식을 복용에 치열하지 않았다, 그래서 나는 결정 WPF 마우스 입력 시스템을 굶기 만하면됩니다. 내가 끝내기 만하면 실제로 업데이트해야 할 때 WriteableBitmap을 업데이트하는 것이 었습니다. 일부 마우스 입력이 영향을 줄 때만 업데이트하면됩니다. 결과적으로 마우스 입력 한 프레임을 수신하고 다음 프레임에서 비트 맵을 업데이트하지만 업데이트가 수 밀리 초가 지나치게 오래 걸리므로 동일한 프레임에서 더 많은 마우스 입력을받지 못하고 다음 프레임을 수신하게됩니다. 마우스를 다시 입력 할 필요가 없으므로 마우스 입력.가변 길이 프레임 시간이 단지 일종의 평균이기 때문에 렌더링 시간이 길어질수록 훨씬 선형적이고 합리적인 성능 저하가 발생합니다.
public class MainWindow : Window
{
private System.Windows.Point _mousePos;
private bool _bitmapNeedsUpdate;
public Window()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (!_bitmapNeedsUpdate) return;
_bitmapNeedsUpdate = false;
// Update my WriteableBitmap here using the _mousePos variable
}
protected override void OnMouseMove(MouseEventArgs e)
{
_mousePos = e.GetPosition(this);
_bitmapNeedsUpdate = true;
base.OnMouseMove(e);
}
}
자신의 특정 상황에이 같은 지식을 번역 : 나는 캐싱의 몇 가지 유형을 시도 할 것입니다 성능 문제가 발생할하여 복잡한 형상에 대한. 예를 들어, Geometry 자체가 변경되지 않거나 자주 변경되지 않으면 RenderTargetBitmap으로 렌더링 한 다음 Geometry 자체를 추가하는 대신 RenderTargetBitmap을 비주얼 트리에 추가하십시오. 이렇게하면 WPF에서 렌더링 경로를 수행 할 때 원시 기하 데이터에서 픽셀 데이터를 다시 작성하는 대신 해당 비트 맵을 blit 처리 만하면됩니다.
당신은 그림 텍스트를 사용하지 않아야합니다. 당신이해야 할 일은 템플릿을'Label' 또는'Textblock'에 적용하고'ItemControl'에 넣고 자동으로 그릴 수 있도록 문자열 배열을 피드하는 것입니다. – Franck
@Franck Thanks Frank, 각 텍스트의 위치와 회전은 어떨까요? – Vahid
템플릿에 모두 포함되어 있습니다. WPF는 X, Y, Z를 뒤집기 위해'ScaleTransform'을 할 수있게 해줍니다. (마지막으로 3dviewport의 경우), 회전은 사용할 태그의 이름이 무엇인지를 기억하지 못합니다.하지만 확실히 변환 그룹에 있습니다. . databinded 개체의 속성에 대한 margin/padding base의 현명한 간단한 오프셋 위치는 충분합니다. – Franck