그릇된 인간의 그릇된 공간 http://dishdev.me/

라이브러리든 프레임워크든 운영체제든 어떤 베이스 위에서 작업을 하다보면

처음엔 "완벽한 세계"가 만들어져 있다는 생각이 든다.

이것도 미리 만들어져 있고 저것도 미리 만들어져 있고...

나는 간단한 인터페이스를 통해 내가 원하는 것을 할 수 있다.

내 행동에 따라 발생할 수 있는 예외 상황은 대부분 미리 예측, 처리되어 있으며

내가 원한다면 특별한 예외 케이스에 대한 처리를 직접 지시할 수도 있다.

그러다보면 그 세계를 믿게 된다.

뭔가 잘못된 경우는 그 세계의 바탕 위에 내가 만든 논리의 잘못이지

세계 아랫단의 잘못이라곤 생각할 수 없게 된다.

...

이런게 좀 심해지면 모에하기도 한다.

(예 : 오오 역시 닭넷 짱이야 /ㅅ/ 하앍하앍)

하지만 이번 일과 같은 일을 겪을 때면

그 절대적인 믿음은 잘못된 것이라는 생각이 마구마구 든다.

라이브러리는 완벽하지 않다.

결국 DX도, VS도, 윈도우도, 네이버도

모두 누군가가 만들어낸 세계에 불과하다.

대부분의 경우 그 누군가는 나보다 똑똑하겠지만 꼭 그러란 법은 없다.

따라서 불합리한 구조와 오동작, 예측불허의 버그 등이

그들의 창조물에 있는 건 놀라운 일이 아닐 것이다.

최근에 겪은 DX 버그 문제는 사실 처음 겪어보는 문제가 아니었다.

내가 하고싶은 건 동영상 재생과 재생되는 동영상 위에 뭔가를 그리고 싶다는 것.

예전에 연대 미대생의 졸업 작품 외주로 OrcasPlayer라는 동영상 플레이어를 만들어준 적이 있는데

그 때 DX로 시도를 하는 과정에 한 번 겪었었고,

이번에 개발 중인 게임 드레드노트의 로비 화면에 동영상을 띠우면서 또 한 번 겪게 되었다.

그 때는 한참 삽질을 하다가 결국 문제 해결을 포기하고 다른 길로 우회해 해답을 찾았다.

당시 베타 테스팅 중이었던 VS2008, 코드명 Orcas를 사용해 원하는 것을 만들 수 있었고

그래서 프로그램 이름이 OrcasPlayer가 되었다.

이번엔... 문제를 해결...했다?

해결했다고 얘기해야 할까. 여기서부터 문제를 어떻게 해결했는지 적을 것인데

보면 알겠지만 참 해결했다고 말하기 민망하다.

서론이 긴데 나이가 들면서 느는 건 이런 것일까 (...)

뭐 이 글을 읽는 사람들도 나이가 들면서 느는 또 하나의 스킬,

글의 "쓰잘데없는 부분"을 재낌으로써 속독하기를 충분히 익혔을 거라 생각하며 (...)

상황은 다음과 같다.

Managed DX를 써서 동영상을 플레이를 쉽게 할 수 있는 구조가

DX 아래에 AudioVideoPlayback이란 곳에 만들어져 있다.

제일 간단히는 원하는 컨트롤에 동영상을 재생할 수 있게 해준다.

각종 동영상 데이타 포맷과 코덱, 재생 타이밍, 버퍼링 문제 등은 모조리 래핑되어 처리되어 있다.

훌륭하다.

컨트롤이 아니라 DX에서 많이 쓰는 텍스쳐로 동영상이 찍혀 나오게 할 수도 있다.

Video 객체의 메소드 RenderToTexture를 통해 할 수 있다.

해보면 잘 된다.

3차원 공간에 동영상을 넣는 것도 그리 어려운 일이 아니다.

하지만 문제는!

RenderToTexture를 사용했을 땐 이 Video 객체의 자원 정리가 제대로 되지 않는다.

(컨트롤에만 찍는 경우엔 잘 된다...)

Dispose 메소드로 자원 정리를 시킬 수 있으며 자원 정리를 명시적으로 하지 않으면

닷넷 프레임워크에서 프로그램이 꺼질 때 알아서 정리한다.

근데 RenderToTexture를 한 번이라도 쓰면 Dispose를 부르는 순간 AccessViolation이 터진다.

그래서 그냥 냅두면? 프로그램이 꺼질 때 닷넷 프레임워크에서 자동으로 정리를 시도하며 터진다.

Dispose를 부르고 AccessViolation 예외를 잡아서 안 터지게 넘기면?

자원 정리 중 예외가 발생한 거라 자원 정리가 제대로 안 된 상태,

결국 프로그램이 꺼질 때 닷넷 프레임워크에서 정리를 또 시도, 폭발한다.

아무리 해도 답이 안 나온다.

이 문제를 피해 갈 길을 제시해주는 인터페이스 역시 없다.

문제를 해결하기 위해 검색을 하다보면 재미있는 자료를 하나 발견할 수 있다.

MSDN에 있는 자료인데 나와 같은 문제를 겪고 있는 사람의 질문 글이다.

질문은 그냥 징징글이고 한 답변이 멋진데 이렇다.

David Weller - NVIDIA

It is a bug, but we have no plans to update this API.

... 헐?

그렇다.

이것은 버그이다. 세계의 결함이다. 당신의 잘못이 아니다.

하지만 고칠 계획이 없댄다.

NVIDIA라고 뒤에 적혀있는 거랑 말하는 걸 보면 이 사람이 DX 개발팀의 일원 쯤 된다는 걸 알 수 있다.

하지만 그런 사람이 고칠 계획이 없댄다.

버그인데도!

예전에도 여기까진 왔었다.

여기서 포기했다.

어떡해.

이 문제가 조물주도 인정한 결함으로부터 생긴 것임을 확인하는 동시에

조물주는 그 결함을 고칠 의사가 없다는 것을 알게 되었는데.

하지만 무슨 생각이었는지 난 이 문제를 더 물고 들었고

결국 이런 자료를 찾게 되었다.

DX 동영상, 음악 재생 쪽 라이브러리를 좀 더 쓰기 쉽게 래핑한 코드이다.

Most commonly, an Access Violation Exception is thrown.

Having done my research on this,

I have learned that this is a bug they are aware of but have no intention of fixing.

...라고 되어 있는 걸 보면 글쓴이도 아마 나와 같은 문제를 겪고

내가 봤던 글을 봤을 것이다.

놀라운 건 그 다음에 이 사람이 문제를 해결(?)하는 방법을 제시하고 있다는 것이다.

방법은 단순했다.

video.Disposing += new EventHandler( VideoDisposing );

void VideoDisposing( object sender, EventArgs e )

{

while( true );

}

!?

이... 이것은 무엇인가.

간단히 말하면 Video 객체가 자원 정리를 시도할 때 프로그램을

무한루프에 빠지게 만드는 것이다.

이런 방식으로 된단 말인가!?

...

된다.

끌 때 1초 이하의 딜레이가 생기는 것 같긴 하지만 터지진 않는다.

와.

이런 걸 시도, 나름의 해결책을 제시한 글쓴이에게 경외를 표한다.

난 while( true )가 아닌 Thread.Sleep( Timeout.Infinite )를 사용했는데 이것도 된다.

속을 보면 이렇다.

AccessViolation이 나는 것은 Video 객체가 Dispose 될 때

RenderToTexture 메소드가 호출된 것과 연계되어 객체 내에서 잘못된 메모리 접근을 하기 때문이다.

따라서 이 Dispose 내부 루틴이 실행되기 직전에 할 일을 지정해줄 수 있는

Disposing 이벤트에서 무한 루프를 돌게 하면 일단 AccessViolation은 나지 않는다.

하지만 이렇게 하면 프로그램이 끝나지 않게 되는데... 어떡함?

보다 더 속사정은 잘 모르지만

DX 관련 자원을 정리할 때 정리 과정이 일정시간 동안 안 끝나면

정리 쓰레드를 알아서 죽여주는 메커니즘이 있나보다.

그래서 해보면 프로그램이 신기하게도 꺼진다 -_-;

뭐 명시적으론 자원 정리가 제대로 안 된 것이라 메모리 릭 같은 문제가 있을 지도 모른다.

그래서 글쓴이는 그닥 추천하지 않으며 트릭키한 방법이라고 말하고 있다.

내가 하나 더 알아낸 웃긴 사실은 Direct3D Device의 생성자를 잘 선택해야 한다는 것이다.

RenderToTexture를 할 때 Device를 넘겨주면서 이쪽과 연관이 생기는데...

여튼 Device 생성자를 보면 주로 쓰는 게 이렇게 둘이 있다.

Device(Int32, DeviceType, IntPtr, CreateFlags, PresentParameters)

Device(Int32, DeviceType, Control, CreateFlags, PresentParameters)

다 똑같고 세 번째 인자만 다른데 이 인자 이름은 renderWindow이다.

DX를 이용해서 렌더링을 할 컨트롤을 넘겨주는 것.

위 생성자는 그 컨트롤의 핸들러(Handle)를 받는 것이고

아래 생성자는 컨트롤 자체를 받는 것이다. 큰 차이는 없다.

하지만 아래 생성자로 Device를 만들면 위에 제시된 방법을 썼을 때

정상적으로(?) 프로그램이 꺼질 때 무한 루프에 빠져서 꺼지지 않게 된다.

따라서 문제를 해결하려면 위 생성자로 Device를 만들어야 한다.

이유는 알 수 없다.

... 정말 웃기다.

그래. 내가 살고 있는 사회에서 사회 구조의 웃기는 점이나

살아가고 있는 인간들이 하고 있는 웃기는 짓을 생각해보면

라이브러리에 이정도는 있어야 인간적인 라이브러리라고 할 수 있겠지.

  1. 飛烏 2009.02.22 Modify Delete Reply # 인간적인 라이브러리는 왠지 신뢰하고 싶지 않은데[...]
    Dish 2009.02.22 Modify Delete # 원래 인간은 믿을 수 없잖아요.. ㅋㅋ
  2. tokki7 2009.02.25 Modify Delete Reply # 결국은 사람이 만든 것....

    가끔 비행기 자동항법 장치나 이런거 걱정될 때가..