2014-12-16 12 views
5

WPF 응용 프로그램에서 SharpDX를 통해 DirectX11을 사용하여 일부 이미지를 부드럽게 스크롤하려고합니다.SharpDX를 사용하여 WPF에서 이미지를 스크롤 할 때 예기치 않은 애니메이션

배경 설명 :

이미지는 텍스처 배열로 D3D로 파일로드 및로드되었는지, 시간 변화, 신호 복귀하고, 화상 자체 질감의 시리즈로 렌더링 quads (매우 긴 수평선에 인접한 일련의 직사각형). 왼쪽 및 오른쪽 가장자리가 시간 오프셋을 나타내도록 뷰포트가 설정되고이 시간 사이의 신호가 표시됩니다. DirectX 구현은 매우 간단합니다. 쿼드 버텍스는 한 번 생성되며, 매우 작은 프레임 당 버퍼에는 현재 가시 범위/줌 등에 따라 스케일과 변환을 업데이트하는 간단한 2D 월드 뷰 변환이 포함됩니다. D3D 구현은 다음과 같습니다. 내가 가지고있는 문제의 일부가 아니다 - 정말 간단한 설정이며 매우 빨리 렌더링되는 것 같다. (필연적으로 디스크에서 텍스처를 스트리밍하는) 약간의 애호가가 있지만 이 모두를 비활성화하고 내가 노력하고 내가 데 문제를 해결하는 동안

가 문제 .. 매우 간단 (작은) 텍스처로 실행하고 있습니다 : 이미지의

스크롤이 원활하지 않을 때 볼 시간 범위 애니메이션입니다. 이미지는 시간의 상당 부분을 "불안하게합니다."그리고 솔직히 끔찍하게 보입니다 (지터가 나지 않을 때는 멋지게 보입니다).

셋업 :

다이렉트는 약간의 작업이 DX11 작업을하기 위해 무대 뒤에서 진행과 더불어, D3DImage로 렌더링 -이 코드는 https://sharpdxwpf.codeplex.com/

에서 가져온 여러 D3DImages는 최대 (이 있습니다 총 4 개)가 격자로 배열되어 있습니다 (둘 다 동일한 시간대의 신호를 포함하고 함께 애니메이션됩니다).

이 D3DI 이미지는 ImageBrush의 소스로 사용되는 DrawingVisual (사용자 지정 FrameworkElement에서 호스팅)에서 그려집니다. 프레임 워크 객체는 필요에 따라 렌더링을 트리거하고 드로잉 비주얼은 D3D 렌더링 호출을 처리하고 D3DImage 브러시를 사용하여 컨트롤을 채울 사각형을 그립니다.

시간 범위 값은 WPF DoubleAnimation을 사용하여 애니메이션으로 표시됩니다. 현재 표시된 비주얼은 INotifyPropertyChanged를 통해이 값에 바인딩되고 각 변경시 렌더를 트리거합니다 (InvalidateVisual을 통해). "위치"값 (볼 시간 범위의 시작)의 변화에 ​​의해 유발

코드를 렌더링 DrawingVisual과는 :

나는 여러 가지를 시도 (및 검색 :

// update scene per-frame buffer first, with world-view transform 
using (DrawingContext dc = RenderOpen() 
{ 
    _scene.Renderer.Render(_draw_args); 
    _image.Invalidate(); 

    dc.DrawRectangle(_brush, null, new Rect(viewer.RenderSize)); 
} 

나는 시도 무엇 이 문제는 렌더링 요청 타이밍이 불안정한 지 또는 문제가 렌더링 프로세스에 더 있는지 여부를 확인하려는 시도입니다.

  1. (우선 CompositionTarget.Rendering 통해 상기 위치 값들의 업데이트를 구동 CompositionTarget.Rendering를 후킹하고, 그대로 공정의 나머지를두고, 즉

(말할 필요도없이,이 매우 많이 "테스트"코드) : 요소는이 값의 변화)에 반응

Stopwatch rsw; 
long last_time = 0; 
void play() 
{ 
    rsw = new Stopwatch(); 
    last_time = 0; 
    rsw.Start(); 
    CompositionTarget.Rendering += rendering; 
} 

void rendering(object sender, EventArgs e) 
{ 
    double update_distance = rsw.ElapsedMilliseconds - last_time; 
    Position += 10 * update_distance; 
    last_time = rsw.ElapsedMilliseconds; 
} 

결과 - 단순한 이중 애니메이션보다 더.

  1. DispatcherTimer 사용. 위와 비슷하게 스톱워치와 함께 타이머를 사용하여 경과 시간을 조금 더 판단합니다. 흥미로운 측면에서 CPU 사용량이 약 7 % (코어 절반)에서 0.7 %로 감소했다는 결과가 다시 나타납니다.

변종 1 & 2와 함께 원래의 시도는 모두 이해 관계자의 렌더링을 유발하는 단일 값을 업데이트하려고합니다. DisptacherTimer가 실제로 가장 일관된 결과를 보냈지 만 (WPF 애니메이션과 CompositionTarget이 홀수 프레임을 떨어 뜨렸지 만) DisptacherTimer가 가장 불안한 애니메이션을 제공 했음에도 불구하고 렌더링 요청 간의 시간 차이가 그리기 시각적 요소에 도달했음을 기록했습니다.

  1. 이미지를 업데이트하지만 시각적 이미지는 무효화하지 않습니다. ImageBrush의 소스를 업데이트하면 DrawingVisual을 다시 렌더링 할 필요없이 표시된 이미지가 업데이트됩니다. 이 방법은 매우 불안정한 결과를 가져 왔습니다.

  2. 컴포지션 대상. 프레임 워크 요소 자체에서 렌더링. 이것은 많은 결과를 가져 왔지만 여전히 완벽하지는 않습니다. 지터가 발생하고 다시 소멸되어 다시 돌아올 수 있습니다. 이 접근 방식에서 DV를 보유하는 프레임 워크 요소는 CompositionTarget.Rendering에 연결되고, 시각적으로는 독립적으로 움직이는 현재 위치를 쿼리합니다. 나는이 접근법으로 거의 살 수 있었다.

  3. D3D 장면이 (더 뚜렷한 개선) 화상을 무효화하지 전에, 렌더링 될 때까지 대기를 시도하는

    _scene.Renderer.Render (_draw_args)를;
    _scene.Renderer.Device.ImmediateContext.End (q);

    while (! (_ scene.Renderer.Device.ImmediateContext.IsDataAvailable()) Thread.Yield();

    _image.Invalidate();

관찰 :

  1. 난 정말이 같은 성능 문제라고 생각하지 않습니다. 내 dev에 기계는 좋은 그래픽 카드, 8 i7 코어 등, 그리고 이것은 간단한 렌더링 작업입니다.
  2. 이것은 실제로 D3D와 WPF 렌더링 사이의 일종의 동기화 문제로 보이지만이 문제를 조사하기 시작하는 방법을 모릅니다.
  3. 두 이미지가 동시에 애니메이션으로 움직이는 경우 지터는 두 이미지 중 첫 번째 이미지 (일반적으로)에서 훨씬 더 두드러집니다.
  4. 애니메이트하는 동안 창 크기를 활발하게 변경하면 D3D 컨텍스트가 계속 크기가 조정되므로 많은 추가 작업이 수행되고 있음에도 불구하고 애니메이션이 완벽하게 부드럽게 처리됩니다.

편집

내가 문제를 시도하고 분리하기 위해 다시 가능한 한 일을 촬영했습니다, 그리고 문제가 D3DImage가 WPF 렌더링 & 업데이트되는 방식에 근본적인 것 같다.

SharpDX Samples 솔루션에서 WPFHost 예제를 수정하여 표시되는 간단한 삼각형이 화면에서 움직이게했습니다. 이 예제는 CompositionTarget.Rendering에서 DPFCanvas에 의해 렌더링되는 DX10ImageSource에서 호스팅됩니다.

이 예제는 더 간단 할 수 없으며 WPF에서 D3DImage를 렌더링하는 동안 얻을 수있는 "금속에 가까운"것으로서 그려졌습니다. 렌더 사이의 시간차로 계산 된 값으로 화면 전체에서 변환되는 단일 삼각형. 마치 어떤 종류의 동기화 문제와 같이 끊임없이오고가는 것입니다. 그것은 나에게 당혹감을 안겨주지 만, SharpDX는 WPF 내에서 사용할 수 없기 때문에 어떤 종류의 부드러운 애니메이션이든 매우 실망 스럽습니다.

사람이이 문제를 재현에 관심이 있다면

의 SharpDX 샘플은 여기에 사용할 수 있습니다

void IScene.Render() 
{ 
... 

     EffectMatrixVariable wv = this.SimpleEffect.GetVariableBySemantic("WorldView").AsMatrix(); 
     wv.SetMatrix(this.WorldViewMatrix); 

... 
} 

void IScene.Update(TimeSpan sceneTime) 
{ 
    float x = (float)sceneTime.Milliseconds * 0.001f - 0.5f; 
    WorldViewMatrix = Matrix.Translation(x, 0, 0); 
} 

및 쉐이더에 Simple.fx :

https://github.com/sharpdx/SharpDX-Samples 내가 WPFHost 예에 다음과 같은 간단한 변경했습니다 :

float4x4 WorldViewTransform : WorldView; 

PS_IN VS(VS_IN input) 
{ 
    PS_IN output = (PS_IN)0; 

    output.pos = mul(input.pos, WorldViewTransform); 
    output.col = input.col * Overlay; 

    return output; 
} 

답변

0

D3DImage 근본적을 나뉩니다.

D3D 위에 XAML 요소를 오버레이 할 필요가 없거나 D3D 표면의 크기를 너무 자주 조정할 필요가없는 경우 HWND/WinForm을 WPF 응용 프로그램에 호스팅하고 일반 렌더링을 사용하여 직접 렌더링하는 것이 좋습니다 고리. 이유에 대한 자세한 내용을 보려면 issue을 확인하십시오.

+1

아야 - 그게 내가 바라던 대답이 아니야! (하지만 어쨌든 고맙습니다.) 그것은 그렇게 할 수 있습니다. 심지어 삼각형을 매끄럽게 애니메이션 할 수 없으며 알려진 해결 방법이없는 경우에도 마찬가지입니다. 실제로 SharpDX를 사용하고 모든 오버레이를 잃어 버리는 꽤 상당한 WPF 앱을 업그레이드하고 있습니다. 능력이 심각하게 제한 될 것입니다. 나는 그 경로가 얼마나 그럴듯한지를 조사 할 것입니다. 그렇지 않으면 우리는 더듬 거리며 살거나 DrawingVisual 접근법으로 되돌아 가야 할 것입니다. (실제로 부드럽지만 다른 방법으로는 제한적입니다.) – Matt