2014-01-22 2 views
3

WriteableBitmap을 사용하여 성능에 민감한 그림이 나타나는 응용 프로그램이 있습니다. 이벤트는 WriteableBitmap의 백 버퍼를 실제로 업데이트하기 위해 CompositionTarget.Rendering으로 호출됩니다. MSDN 설명서에서 이벤트가 프레임 당 한 번, 컨트롤이 렌더링되기 바로 전에 발생한다는 것을 의미합니다.WriteableBitmap.Lock() 성능 문제

내가 겪고있는 문제는 WriteableBitmap의 Lock() 함수가 특히 더 큰 비트 맵 크기에서 매우 오랜 시간이 걸린다는 것입니다. 이전에 AddDirtyRegion()에는 전체 비트 맵이 무효화되는 버그가있어서 성능이 저하되었다고 읽었습니다. 그러나, 여기서는 그렇지 않습니다. 저수준 검사의 좋은 비트에서, Lock()은 렌더링 스레드에 쓰기 위해 비트 맵의 ​​백 버퍼를 여는 것으로 보입니다. 이는 내 이벤트 핸들러가 호출 될 때마다 렌더링 스레드가 준비 될 때까지 스레드를 블록해야한다는 것을 의미합니다. 이로 인해 비트 맵의 ​​그래픽을 업데이트 할 때 눈에 띄는 지연이 발생합니다.

이미 이벤트 처리기에 TryLock()을 사용하여 시간 초과를 추가 했으므로 오랜 시간 동안 차단되지 않고 성능이 저하됩니다. 그러나 비트 맵 업데이트 수가 많아지기 때문에 지연되는 것처럼 보입니다.

다음은 이벤트 처리기의 관련 코드로 내가하는 작업을 정확하게 보여줍니다. UpdatePixels() 기능은 사용하지 않는 것이 기록 된 잠재적 AddDirtyRect() 도청 :

void updateBitmap(object sender, EventArgs e) 
{ 
    if (!form.ResizingWindow) 
    { 
     // Lock and unlock are important... Make sure to keep them outside of the loop for performance reasons. 
     if (canvasUpdates.Count > 0) 
     { 
      //bool locked = scaledDrawingMap.TryLock(bitmapLockDuration); 
      scaledDrawingMap.Lock(); 
      //if (locked) 
      //{ 
      unsafe 
      { 
       int* pixData = (int*)scaledDrawingMap.BackBuffer; 
       foreach (Int32Rect i in canvasUpdates) 
       { 
        // The graphics object isn't directly shown, so this isn't actually necessary. We do a sort of manual copy from the drawingMap, which acts similarly 
        // to a back buffer. 
        Int32Rect temp = GetValidDirtyRegion(i); 
        UpdatePixels(temp, pixData); 
       } 
       scaledDrawingMap.Unlock(); 
       canvasUpdates.Clear(); 
      } 
      //} 
     } 
    } 
} 

private unsafe void UpdatePixels(Int32Rect temp, int* pixData) 
{ 
    //int* pixData = (int*)scaledDrawingMap.BackBuffer; 
    // Directly copy the backbuffer into a new buffer, to use WritePixels(). 
    var stride = temp.Width * scaledDrawingMap.Format.BitsPerPixel/8; 
    int[] relevantPixData = new int[stride * temp.Height]; 
    int srcIdx = 0; 
    int pWidth = scaledDrawingMap.PixelWidth; 
    int yLess = temp.Y + temp.Height; 
    int xLess = temp.X + temp.Width; 
    for (int y = temp.Y; y < yLess; y++) 
    { 
     for (int x = temp.X; x < xLess; x++) 
     { 
      relevantPixData[srcIdx++] = pixData[y * pWidth + x]; 
     } 
    } 
    scaledDrawingMap.WritePixels(temp, relevantPixData, stride, 0); 
} 

나는 WriteableBitmap으로 차단 스레드의 문제를 방지하는 방법을 알아낼 수없는 것, 내가 어떤 명백한 결함을 볼 수 없습니다 내가 작성한 코드. 어떤 도움이나 조언을 많이 주시면 감사하겠습니다.

+0

그동안 해결책을 찾았습니까? –

답변

0

실제로 읽기 전용으로 쓰기 위해 BackBuffer를 사용하지 않는 것처럼 보입니다.
WritePixels는 "전면"버퍼에 쓰고 잠금을 필요로하지 않습니다. 다른 이유로 잠금을 걸어야하는 다른 이유가 있는지 모르겠지만 여기에있는 코드는 왜 필요한지 알 수 없습니다.

+0

최소한 내 상황에서는 비트 맵을 먼저 잠그지 않으면 WritePixels에서 예외가 발생합니다. –

0

내가 BackBuffer (* pixData)에서 읽으려는 잠금 장치가 필요 없다고 생각한다. 필자는 쓰기 전용이라고 생각했지만 WritePackels에 Lock을 호출 할 필요가 없다고 생각한다.

지금까지 내가, 당신이 일을 말할 수 :

  1. 가 다시 잠금을 해제 새로운 배열
  2. 를 사용하여 배열에 백 버퍼를
  3. 전화 WritePixels을 그것에서
  4. 복사 뭔가를 잠금 완충기.

전환 3과 4는 어떨까요?

WritePixels는 내부적으로 렌더링 스레드 (메시지 큐에서 우선 순위가 높음)가 지연을 유발하는 요인 일 수있는 잠금을 생성 할 수 있습니다.

+0

지난 번에 남긴 코멘트가 정확하지 않았습니다. AddDirtyRect는 비트 맵을 호출 할 때 잠겨 있지 않은 경우 예외를 throw하는 함수입니다. 쓰기가 이미 잠겨 있지 않은 경우 비트 맵을 잠급니다.당신이 제안하는 것은 backbuffer를 잠그고, 픽셀을 복사하고, backbuffer를 잠금 해제 한 다음 잠그고 다시 쓰기를 해제합니다 (Write 호출의 경우). 그건 나에게 비효율적 인 것 같아. –

+0

게시 한 코드가 AddDirtyRect를 호출하지 않습니다 ... –

+0

맞아요,하지만 그 기능은 내가 켜고 끄는 것과 비슷한 기능입니다. 이 주석을 게시 한 시간이 매우 빈번히 앞뒤로 움직일 때를 감안할 때 두 가지 접근법에 대한 몇 가지주의 사항이 혼재되어 있습니다. –