실버라이트 애플리케이션 위에 HTML 엘리먼트를 올리기

실버라이트 애플리케이션을 만들다가 이런 생각을 해보신 적이 있을거에요.

'실버라이트 영역 위에 다른 HTML DIV를 올릴 수 있지 않을까? 어차피 실버라이트 애플리케이션도 HTML 태그일 뿐이니까!'

전 곧바로 새 프로젝트를 만들고 이런 코드를 작성하겠죠.

<div id="SilverlightControlHost" class="silverlightHost" >
    <script type="text/javascript">
        createSilverlight();
    </script>
</div>
<div style="position:absolute; left:0px; top:0px; height:100px; background-color:#CCCCCC;">
    I'm floating DIV! What's your name? : <input id="name" type="text" size="50" />
</div>

 

F5를 눌러보곤 '안 되잖아! 뭐가 잘못된 거지!?' 라고 실망할 거에요.

실버라이트 애플리케이션은 기본적으로 HTML 페이지에 OBJECT 태그를 통해 호스팅되는 HTML 컨트롤이라고 할 수 있어요. 실버라이트 애플리케이션의 인스턴스는 기본 프로젝트 템플릿에 포함된 createSilverlight()라는 함수를 통해 생성할 수 있죠. createSilverlight() 함수는 내부적으로 Silverlight.js가 제공하는 Silverlight.createObjectEx()라는 메서드를 호출하고 이 때, 실버라이트 애플리케이션의 동작 환경과 특성을 결정하는 여러가지 속성을 함께 전달할 수 는데, 이 중 isWindowless 라는 속성을 "true"로 설정하면 비로소 HTML 요소들과 마찬가지로 실버라이트 애플리케이션의 영역위에 다른 HTML 요소를 '띄울 수' 있어요.

그러면 다른 코드는 그대로 두고 createSilverlight() 함수에 이 마법의 키워드, isWindowless를 "true"로 설정해보죠.

function createSilverlight()
{
    Silverlight.createObjectEx({
        source: "Page.xaml",
        parentElement: document.getElementById("SilverlightControlHost"),
        id: "SilverlightControl",
        properties: {
            width: "100%",
            height: "100%",
            version: "1.1",
            isWindowless: "true",
            enableHtmlAccess: "true"
        },
        events: {}
    });
    // 다른 코드 생략
}

와우! HTML DIV가 그대로 보이네요. 도대체 isWindowless 뒤에는 무슨일이 있었던 걸까요?

이 글에서는 isWindowless을 통해 실버라이트 애플리케이션의 렌더링 방식에 대해 조금 자세히 알아볼께요.
이 글에서 사용될 샘플 프로젝트를 다운받으셔서 따라오는 것도 좋은 방법이에요. 다운로드는 아래에서.


기본적인 실버라이트 애플리케이션 렌더링

한 가지 생각을 해보죠. 실버라이트 애플리케이션은 웹 브라우저 위에 OBJECT 태그로 호스팅되고 있지만 웹 브라우저는 실버라이트 애플리케이션을 어떻게 렌더링해야 하는지 이해할 수 없을 거에요. 당연히 실버라이트 애플리케이션의 영역은 실버라이트 런타임이 렌더링할 책임을 가지고 있을 거에요.

기본적으로 실버라이트 런타임은 실버라이트 애플리케이션의 인스턴스를 생성할 때 자신이 직접 컨트롤하는 'Window'의 인스턴스를 생성하여 자신을 호스팅하는 HTML 페이지를 렌더링하는 Window의 자식 윈도(child window)로 추가를 해요. 그리고 사용자의 명시적인 코드-Win32 프로그래밍에서 많은 개발자를 골치아프게 만들었던 WM_PAINT 메시지 핸들러 따위- 없이도 실버라이트 런타임에 내장된 그래픽 엔진이 XAML을 해석하여 적절한 시점에 적절한 그래픽을 렌더링해 주죠.

실버라이트 런타임이 개발자의 머리속을 헤집어 놓지 않고도 똑똑하게 렌더링을 하지만 한가지 문제가 생겼어요. 바로 렌더링 할 영역이 새로운 윈도가 되기 때문에 기존의 HTML 엘리먼트들을 렌더링하는 윈도와는 별개로 취급되고 다른 HTML 엘리먼트들을 덮어버리게 되는 것이죠. 왜냐하면 윈도우즈 OS의 렌더링은 윈도 단위로 일어나기 때문이에요. 만약 Win32 프로그래밍 경험이 없다면 지금 무슨 얘기를 하는건지 도무지 이해할 수 없을 거에요. 하지만 제가 늘 얘기하듯이 쫄지 마세요! 이해가 되지 않는 부분은 과감하게 무시해도 좋아요. 다만 이해되지 않더라도 세번은 읽어보세요. 언젠가 이것이 이해가 되는 날이 올테니까요.

어쨌든, Spy++라는 슈퍼 유용한 툴을 사용하여 이 상황을 자세히 뜯어보죠. Spy++는 비주얼 스튜디오에 포함된 툴로 모든 윈도와 스레드, 프로세스 및 이들 사이를 왔다갔다하는 메시지들을 훔쳐 볼 수 있어요. Spy++은 기본적으로 비주얼 스튜디오가 설치된 폴더 아래에 Common7\Tools\spyxx.exe 라는 이름으로 존재해요. 위에서 만들었던 애플리케이션에서 isWindowless 설정을 주석처리하여 기본 상태로 실행하고 Spy++로 실행된 웹 브라우저의 윈도 상태를 뜯어볼까요? Spy++를 잘 모르시는 분을 위해 이 과정을 녹화해봤으니 참고하세요. (원본이 800x600이니 다운 받아서 보세요 : http://gongdo.tistory.com/attachment/cfile29.uf@276A30415878F5FA1F2BA7.wmv)


이 그림은 IE7에서 실행한 실버라이트 애플리케이션이 익스플로러의 HTML을 렌더링 하는 윈도인 "Internet Explorer_Server"의 자식 윈도로 포함되어 있음을 보여주고 있어요. 또한 "Internet Explorer_Server"에는 오직 실버라이트 애플리케이션만이 포함되어 있으므로 다른 HTML 엘리먼트들은 별도의 윈도가 아니라 Internet Explorer_Server에서 렌더링 된다는 것을 짐작할 수 있죠.

그럼 파이어폭스에서는 어떤 모습일까요? 한 번 훔쳐보도록 하죠.

익스플로러처럼 윈도의 이름이 명확하지 않아서 잘 구분은 가지 않지만, 분명히 실버라이트 영역이 별도의 윈도로 잡히는 것을 확인할 수 있을 거에요.

Windowless 모드에서의 렌더링

자, 그럼 똑같은 데모를 isWindowless 속성을 "true"로 설정하여 실행하고 Spy++로 열어볼게요. (역시 원본은 800x600 : http://gongdo.tistory.com/attachment/cfile23.uf@236314425878F5F70CB66C.wmv)


네, Window 모드와는 달리 "Internet Explorer_Server" 윈도 아래에 아무것도 없는 것을 확인할 수 있어요. 즉, 실버라이트 애플리케이션은 다른 HTML 엘리먼트들과 같은 윈도에 렌더링된다는 거죠. 파이어폭스에서도 확인해 볼까요?

역시나 이름이 구분이 안가지만 Window 모드일 때 8단계였던 윈도들이 Windowless 모드일 때에는 7단계로 줄어있는 것을 확인할 수 있어요. 바로 실버라이트 애플리케이션을 렌더링하던 윈도가 없어진 거죠.

Window vs Windowless

자 이제 결전의 시간입니다!

Window 모드와 Windowless 모드 중 어느 쪽을 사용하는 게 더 좋을까요?

'당연히 다른 HTML 엘리먼트와 조화할 수 있는 Windowless 모드가 더 좋죠!'

당연히 그렇겠죠? 하지만 Windowless 모드에는 렌더링 방식의 차이에서 오는 치명적인 문제가 있어요. 바로 성능 저하죠.

이 부분에 대해서는 마이크로소프트에서 확실하게 문서화된 내용이 없어서 정확하지는 않지만 저는 이렇게 추측하고 있어요. 실버라이트 런타임이 실버라이트 애플리케이션을 Windowless 모드로 생성하였을 때 실버라이트 애플리케이션은 다른 HTML 엘리먼트와 동일한 윈도에 자리를 잡게 되겠죠. 아마도 실버라이트 런타임은 이 윈도의 WM_PAINT 메시지를 서브클래싱 하여 HTML 영역이 다시 그려질 때 실버라이트 애플리케이션이 정상적으로 보일 수 있도록 렌더링하고 또한 애플리케이션 자체의 애니메이션 등으로 그래픽의 변화가 있을 때에도 해당 영역을 다시 렌더링 할거에요. 일반적으로 서브클래싱은 윈도의 소유자가 직접 처리하는 윈도 프로시저에 비해 더 느린걸로 알려져 있어요. 당연히 일반적인 렌더링에 비해 한 단계 더 거치니까 느려지겠죠. 하지만 이 차이는 지금의 하드웨어 스펙을 고려해보면 사실상 거의 무의미한 차이일 거에요.

성능 저하 문제를 논할 정도로 확실한 차이를 보이는 것은 단순한 렌더링이 아닌 색상의 불투명도를 나타내는 알파값이 100%보다 낮을 때 즉, 렌더링해야 할 HTML 엘리먼트와 실버라이트 영역이 서로 색상값이 섞일 때이죠. 일반적으로 2D 그래픽 처리에서 가장 CPU 리소스를 많이 먹는 작업이 바로 알파 블렌딩 작업이라고 해요.

※물론 원형 그래디언트나 폰트의 안티앨리어싱 같은 고도의 연산을 필요로 하는 처리도 있지만 이런 처리의 일부에도 역시 알파 블렌딩이 들어가기 때문에 위와 같은 표현을 사용했어요.

또 한가지, Windowless 모드일 때에는 실버라이트 런타임이 윈도를 완전히 제어하지 않기 때문에 윈도를 다시 렌더링할 적절한 시점을 맞추지 못할 수 있어요. 특히 실버라이트 애플리케이션이 애니메이션을 포함하고 있을 경우 최악의 경우엔 애니메이션이 깜빡거리는 현상의 원인이 되기도 하죠.

반면 Window 모드에서는 아무리 XAML의 최상위 Canvas를 투명으로 설정한다고 해도 실버라이트 애플리케이션이 렌더링 되는 윈도 자체의 배경 색상이 있기 때문에 알파값은 100%가 되고 알파 블렌딩은 발생하지 않게 되죠. 또한 렌더링에 고려할 요소가 정확히 실버라이트 런타임이 제어하는 윈도로 한정이 되기 때문에 애니메이션 등의 복잡한 렌더링을 높은 효율로 처리할 수 있게될 거에요.

결론

항상 강조하는 것.

실버라이트는 HTML 페이지에 호스팅되는 웹 애플리케이션이다.

따라서 필요하다면 Windowless 모드를 사용하여 다른 HTML 엘리먼트들과 계층을 이룰 수 있겠지만, 앞에서 살펴본 것 처럼 Windowless 모드는 많은 대가를 치르게 되므로 가능한 실버라이트 애플리케이션이 자신의 영역을 완전히 차지하도록 디자인 하는 것이 성능상 이득이 있다는 것을 잊지마세요.

하지만! 너무 겁먹지는 마세요. 실버라이트는 웹 애플리케이션, 그 중에서도 RIA를 위해 나왔잖아요? 멋지고 풍부한 그래픽의 디자인을 지레 겁먹고 죽일 필요는 없을 거에요. 해보지도 않고 상상력을 제한하지 마세요. 한 번 시도해 보세요. 그리고 애플리케이션이 목표로 하는 성능에 부합하는지 테스트 할 수 있을 거에요. 어떤가요? 물론 Window 모드에 비해 CPU 점유율이 높을 수 있을 거에요. 하지만 생각보다 부드럽게 움직이지 않나요? 의외로 CPU 점유율 차이도 많지 않을 수도 있어요. :D

참고

  • MSDN : Windowless 속성
  • 김상형저 Win32 API : 초급 강좌 / 윈도우즈 환경에서의 프로그래밍 기초를 다지는데 큰 도움이 될 거에요.
  • MSDN 허접 번역 : WPF의 렌더링 동작 특성 / 비록 WPF의 렌더링 과정에 대해 설명하는 내용이지만 실버라이트도 WPF와 유사한 그래픽 모델을 가지고 있으므로 참고가 될 거에요. (그런데 너무 오래전에 한거라 번역 정말 형편없네요 ㅠ.ㅜ)
  • 실버라이트 성능 향상 팁 / 피가 되고 살이 될 팁! 특히 Framerate 제한은 필독!
  • MSDN의 성능 향상 팁 / 영문도 한 번 읽어보세요.

신고
Posted by gongdo

스터디 그룹 은광여고에서 발표하기 위해 작성한 PPT 자료에요.

WPF와는 달리 실버라잇의 내부 렌더링 과정은 문서화되지 않았는데요, 아마도 WPF와 거의 유사한 구조로 되어 있을 거라고 '추측'하고 작성한 자료에요. 실제 구조와 거리가 있을 수 있다는 건 염두하셔야 할 것 같네요.

처음에 설명하는 개체 모델에 관한건 특별한 내용은 없구요 다만 아래의 내용으로 정리할 수 있을 것 같아요.

1. 모든 컨트롤은 최상위의 DependencyObject로부터 파생되어 Visual ->UIElement->FrameworkElement 순서의 상속 구조를 갖습니다.
2. WPF의 광범위한 지원과 달리 실버라이트의 Visual은 화면에 표시될 수 있는 최소 단위를 의미합니다. 하지만 사용자가 직접 만들 수는 없습니다.
3. UIElement는 마우스나 키보드 이벤트 등의 주요 인터랙션과 Transform 인터페이스를 제공합니다.
4. FrameworkElement는 자신을 포함하는 컨테이너를 반환하는 Parent 속성을 제공하며 UI구성 요소의 실질적인 최소 단위가 됩니다.
5. Panel은 Children 속성을 통해 개체 컨테이너를 제공하는 실버라이트의 유일한 컨테이너이며 Canvas는 이 Panel을 상속하고 자식 노드의 좌표를 관리하는 DependencyProperty를 제공합니다.

아래는 Silverlight의 렌더링 과정을 추측해 본 건데요, 추측이라기 보담 WPF의 렌더링 모델에서 실버라잇이 지원하지 않는 것을 뺀 것 뿐이에요.


그런데 꿍님의 의견에 의하면 실제로 렌더링 과정에서 Clip이 가장 먼저 일어나는 것 같다고 하네요. 뭐 상식적으로 생각해도 당연한 것일 것 같아요. 여튼 어느쪽이든 지금으로선 정확히 문서화 되지 않았으니 너무 깊이 생각할 필요는 없을 것 같네요.

렌더링에서 중요한 점은 실버라잇의 비주얼 개체들의 속성을 변경했다고 해서 그것이 변경하는 순간 즉시 반영되지 않는다는 점이에요. 이 점역시 WPF의 렌더링 모델을 통한 추측일 뿐이지만요.

실버라잇의 비주얼 개체들의 속성을 변경하는 것은 메모리 상의 속성 값만 바뀐 상태이고 이것을 실제로 렌더링 하는 시점은 실버라잇의 런타임이 관리하는 것으로 생각되요.
이 생각이 맞다면 코드 내에서 속성 값을 수정하는 데 화면의 변경시점을 노심초사할 필요가 없어지겠죠. 단일 코드블럭(한 프로시저)내에서의 속성 수정은 그 도중에 화면 업데이트가 일어나지 않을 거에요.

발표때 미처 얘기 못한 내용이지만, 보통 Win32 환경에서 렌더링이란 것은 윈도 프로시저가 WM_PAINT 메시지를 통해 처리하게 되죠. Win32 개발에서는 이 WM_PAINT메시지를 개발자가 임의의 시점에 발생시킬 수 있기 때문에 UI 컴포넌트에 따라 그려지는 시점 특성이 다를 수가 있어요.

자료가 어제 자기 전에 급조한거라 부실하니 그냥 참고 정도로 보시면 될 것 같아요.
에고 이번에는 미뤄왔던 퀵스타츠나 스토리보드쪽 얘기를 하고 싶었는데 영 시간이 안나요.

신고
Posted by gongdo


티스토리 툴바