<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>dieBuster Flash</title>
	<atom:link href="http://www.diebuster.com/flash/feed" rel="self" type="application/rss+xml" />
	<link>http://www.diebuster.com/flash</link>
	<description>programming actionscript 3</description>
	<lastBuildDate>Wed, 21 Dec 2011 10:32:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Away3D 3.6</title>
		<link>http://www.diebuster.com/flash/510</link>
		<comments>http://www.diebuster.com/flash/510#comments</comments>
		<pubDate>Wed, 21 Dec 2011 10:27:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[4 3D]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=510</guid>
		<description><![CDATA[제가 번역한 책이 곧 출간되는군요. 파워플 멤버 모두 수고 많았음. 그간 날 믿고 잘 따라와줘서 고맙고 ^^ 팀원들 모두에게 좋은 선물과 기억이 되길.]]></description>
			<content:encoded><![CDATA[<p><img src="https://lh5.googleusercontent.com/-ykbTINTGuSc/TvGzPebmynI/AAAAAAAAAbk/yTEVitt6lTI/s512/Away3D%2525203.6%252520Essentials%252520%2525ED%252595%25259C%2525EA%2525B5%2525AD%2525EC%252596%2525B4%2525ED%25258C%252590_%2525ED%252591%25259C%2525EC%2525A7%2525801.jpg" alt="" /></p>
<p>제가 번역한 책이 곧 출간되는군요.</p>
<p>파워플 멤버 모두 수고 많았음. 그간 날 믿고 잘 따라와줘서 고맙고 ^^</p>
<p>팀원들 모두에게 좋은 선물과 기억이 되길.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/510/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Loop와 getTimer</title>
		<link>http://www.diebuster.com/flash/502</link>
		<comments>http://www.diebuster.com/flash/502#comments</comments>
		<pubDate>Sat, 26 Nov 2011 05:04:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[3 흐름제어]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=502</guid>
		<description><![CDATA[전에 작성한 포스트 중 다음과 같은 글이 있습니다. Non blocking loop 이 글에서 동기화 로직인 루프로 인해 프로그램이 타임아웃으로 다운되거나 먹통이 되는 현상을 어떻게 처리하는지 살펴봤습니다. 오늘은 약간 다른 접근을 해보려고 합니다. 동기화 로직인 for나 while을 사용해도 적절한 시점에 빠져 나오는 기법입니다. 이 방법을 사용하면 특정 반복구문 하나가 시간을 계속 끌지 않게 되어 스크립트 타임아웃이 [...]]]></description>
			<content:encoded><![CDATA[<p>전에 작성한 포스트 중 다음과 같은 글이 있습니다.</p>
<p><a href="http://www.diebuster.com/flash/180" target="_blank">Non blocking loop</a></p>
<p>이 글에서 동기화 로직인 루프로 인해 프로그램이 타임아웃으로 다운되거나 먹통이 되는 현상을 어떻게 처리하는지 살펴봤습니다.</p>
<p>오늘은 약간 다른 접근을 해보려고 합니다.</p>
<p>동기화 로직인 for나 while을 사용해도 적절한 시점에 빠져 나오는 기법입니다.</p>
<p>이 방법을 사용하면 특정 반복구문 하나가 시간을 계속 끌지 않게 되어 스크립트 타임아웃이 일어나지 않습니다. 단지 먹통 자체는 그대로 일어납니다.</p>
<p><span id="more-502"></span></p>
<h4>간단한 while구문</h4>
<p>동기화로 먹통을 만드는 간단한 while구문을 하나 작성해보죠.</p>
<pre class="brush: as3; title: ; notranslate">
var count:int, arr:Array;

arr = [];

while( count &amp;lt; 10000000 ){

	arr[count] = count;
	count = count + 1;

}
</pre>
<p>간단하지만 컴터의 cpu성능에 따라 꽤나 먹통으로 만들어줍니다. 게다가 15초를 넘어가면 타임아웃이니 더 큰 숫자를 넣어야 할 경우는 어찌해야 하냐는 거죠.</p>
<h4>하지만 시간은 흐른다.</h4>
<p>그렇습니다. 컴터는 반복처리를 하느라 먹통이지만 시간은 흐릅니다. 타이머는 내부적으로 avm2가 갖고 있는 속성이라기보다 플래시 플레이어 수준에서 OS에게 받아오는 값입니다(그래서 getTimer호출은 비용이 꽤 듭니다)</p>
<p>따라서 동기화로직을 처리하는건 avm2의 사정이고 OS수준에서의 시간은 계속 흐르고 있으므로 루프 중에도 시간을 조회해보면 갱신되고 있습니다. 그걸 코드로 표현하면 아래와 같겠죠.</p>
<pre class="brush: as3; title: ; notranslate">
var count:int, arr:Array;

arr = [];

while( count &amp;lt; 10000000 ){

	arr[count] = count;
	count = count + 1;

	switch( count ){
	case 1000000: case 2000000: case 3000000: case 4000000: case 5000000:
	case 6000000: case 7000000: case 8000000: case 9000000:
		trace( getTimer() );
	}
}
</pre>
<p>백만카운터니 십만단위로 시간을 체크해봅시다. 그럼 trace에 찍힌 값이 점점 증가하고 있는걸 알 수 있습니다. 만약 여러분의 컴터가 너무 고속이거나 저속이라면 적당히 카운터를 조정하세요.</p>
<p>위의 결과를 보면 while중에도 여전히 getTimer를 이용해 탈출할 수 있다는 사실을 알 수 있습니다. 최초 시작시간으로 부터 1초가 지나면 탈출하게 작성하면 다음과 같습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var count:int, arr:Array, start:uint;

arr = [];
start = getTimer();

loop: while( count &amp;lt; 10000000 ){

	arr[count] = count;
	count = count + 1;
	switch( count ){
	case 1000000: case 2000000: case 3000000: case 4000000: case 5000000:
	case 6000000: case 7000000: case 8000000: case 9000000:
		if( getTimer() &amp;gt; start + 1000 ){
			break loop;
		}
	}
}
</pre>
<h4>루프를 빠져 나온 뒤의 문제</h4>
<p>루프는 타임아웃이 걸리기 전에 탈출할 수 있지만 그렇다고 문제가 해결된 것은 아닙니다. 왜냐면 스크립트 엔진은 우리가 생각하는 것보다 지능적이라서 단일 반복문이 타임아웃거리는 상황만 감시하는게 아니라 명령스택 전체를 감시하기 때문입니다.</p>
<p>위의 상황에서 2십만번째에 탈출해도 다시 2십만 1에서 시작하려고 하면 다음과 같은 형태가 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
while(...){
	...
}
while(...){
	...
}
</pre>
<p>혹은 함수로 호출해도 마찬가지입니다.</p>
<pre class="brush: as3; title: ; notranslate">
function loop():Boolean{
	while(...){
		...
	}
	return ( 여전히 반복해야 하나? )
}

function action():void{
	if( loop() ){
		action();
	}
}

action();
</pre>
<p>이런식으로 빙빙 돌려 중첩 루프가 아니게 만들어도 똘똘한 스크립트엔진은 한 번에 실행될 동기구문의 전체 스택을 파악하여 시간을 감시하기 때문에 여전히 타임아웃이 걸리게 됩니다.</p>
<h4>비동기와의 결합</h4>
<p>따라서 결국 끊어야 합니다. 동기화 실행의 한계는 여전히 있는 거죠. 그럼 기존에 다뤘던 넌브로킹 루프와의 차이는 뭘까요?</p>
<p>만약 while안에서 getTimer로 끊어내지 않는다면 개발자가 매번 여러 가지 상황에 따라 반복할 횟수를 정해야 합니다. 또한 그러한 횟수는 기계의 성능에 따라 달라지기 때문에 설정 값 자체가 쓸모 없게 되는 경우도 발생합니다. 넌브로킹 루프의 샘플을 개선해봅시다.</p>
<p>기존 소스를 보면 두번째 인자로 리미트를 받습니다. 한프레임에서 얼마나 실행할지를 지정하는 것이죠. 하지만 이게 바로 문제의 근원입니다.</p>
<pre class="brush: as3; title: ; notranslate">
class nonBlockingLoop{

	var looper:Shape = new Shape;

	public function nonBlockingLoop(){}

	public function loop( $counter:int, $limit:int, $runner:Function, ...$param ):void{
		var counter:int;
		looper.addEventListener( Event.ENTER_FRAME, function( $e:Event ):void{
			var i:int;
			while( i++ &amp;lt; $limit ){
				if( counter &amp;lt; $counter ){
					//$param.unshift( counter ); 필요하면 counter도 넘...
					if( $param.length ){ //rest인자는 null인 경우가 없음
						$runner.apply( null, $param );
					}else{
						$runner();
					}
					++counter;
				}else{
					looper.removeEventListener( $e.type, arguments.callee );
				}
			}
		});
	}
}
</pre>
<p>리미트에 적당한 값을 지정하는건 컴터마다 성능이 다르기 때문에 불가능하기 때문입니다. 따라서 가장 적합한 값은 숫자가 아니라 시간입니다. 두번째인자를  $limit에서 $limitTime으로 변경해보겠습니다.</p>
<pre class="brush: as3; title: ; notranslate">
class nonBlockingLoop{

	var looper:Shape = new Shape;

	public function nonBlockingLoop(){}

	public function loop( $counter:int, $limitTime:int, $runner:Function, ...$param ):void{
		var counter:int;
		looper.addEventListener( Event.ENTER_FRAME, function( $e:Event ):void{
			var start:int;

			start = getTimer();
			while( getTimer() &amp;lt; start + $limitTime ){
				if( counter &amp;lt; $counter ){
					if( $param.length ){
						$runner.apply( null, $param );
					}else{
						$runner();
					}
					++counter;
				}else{
					looper.removeEventListener( $e.type, arguments.callee );
				}
			}
		});
	}
}
</pre>
<p>루프에서 시간을 체크하는 로직으로 변경하여 더 이상 컴터마다 카운터를 따로 지정해야하는 애매함을 제거하고 공통적으로 시간당 처리능력에 따라 적절히 분산되는 코드로 변경되었습니다.<br />
호스트 코드로 보면 다음과 같습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var looper:nonBlockingLoop = new nonBlockingLoop;

function test( $val1:String, $val2:int ):void{
	trace( $val1, $val2 );
}

looper.loop( 999999, 5000, test, '안녕', 37 ); //반복하다 5초가 넘어가면 다음 프레임으로 넘긴다.
</pre>
<h4>결론</h4>
<p>동기화 로직 중에 시간을 이용해 빠져나오는 기술은 개별 컴터의 성능을 따로 측정하지 않아도 일정한 부하만 주며 반복작업을 처리할 수 있게 해주는 강력한 방법입니다.<br />
일단 동기화 로직 중에 빠져나올 근거가 단순한 변수 외에도 시간을 사용할 수 있다는 점 자체만 해도 매우 다양한 알고리즘을 짤 수 있게 합니다. 꼭 <a href="http://www.diebuster.com/flash/180" target="_blank">Non blocking loop</a> 와 이어서 보시길 바랍니다 ^^</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/502/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Alternativa3D 8.x의 구조와 bs11 #3</title>
		<link>http://www.diebuster.com/flash/486</link>
		<comments>http://www.diebuster.com/flash/486#comments</comments>
		<pubDate>Tue, 18 Oct 2011 15:32:43 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[bs11]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=486</guid>
		<description><![CDATA[처음부터 너무 설명만 지루하게 한 감이 없지 않습니다. 사실 3D라는 건 눈으로 보고 마우스로 만지기 위해 존재하는 거지 머리만 아프려고 공부하는 건 아니겠죠. 이 쯤에서 걍 심심풀이 구경이나 좀 하는 시간을 갖도록 하죠. 아래 공개된 작업물들은 개발과정에서 나온 일종의 부산물입니다. 그냥 버릴 수도 있지만 이게 다 역사 될지 누가 알겠습니까 ^^; 알파테스트1차 이 버전은 매우 [...]]]></description>
			<content:encoded><![CDATA[<p>처음부터 너무 설명만 지루하게 한 감이 없지 않습니다. 사실 3D라는 건 눈으로 보고 마우스로 만지기 위해 존재하는 거지 머리만 아프려고 공부하는 건 아니겠죠. 이 쯤에서 걍 심심풀이 구경이나 좀 하는 시간을 갖도록 하죠. 아래 공개된 작업물들은 개발과정에서 나온 일종의 부산물입니다. 그냥 버릴 수도 있지만 이게 다 역사 될지 누가 알겠습니까 ^^;<br />
<span id="more-486"></span></p>
<h4>알파테스트1차</h4>
<p><a href="http://www.diebuster.com/lab/alpha0/" target="_blank"><img src="https://lh6.googleusercontent.com/-_gX3kGx1P7o/Tp2XfZAnInI/AAAAAAAAAPY/lQy0PRhefG4/s500/Screenshot_5.jpg" alt="" /></a></p>
<p>이 버전은 매우 초창기 프로토타입 입니다. 여러 얼터8의 내부 기능을 조사 중이었습니다. 최초 마우스를 누른 채 움직이면 카메라가 회전합니다만 그 이후 왼쪽 빨간 박스나 오른쪽의 로딩된 모델을 클릭하면 그 클릭한 대상을 회전하게 됩니다. 이런 저런 실험과 인터페이스 개선을 하고 있던 시기입니다.</p>
<h4>Geomerty의 공유 실험</h4>
<p>포스팅에 쓴 대로 정말 그런 일이 일어나는지 보기 위해 작은 실험을 했습니다. 위에 있는 것이 공유를 했을 때 메모리가 늘어나는 정도를 보여준다면 아래 있는 것은 공유하지 않고 Box를 생성할 때마다 Geometry를 upload하는 경우라 볼 수 있습니다. 물론 두 번째가 메모리를 훨씬 크게 잠식합니다.</p>
<p><strong>Geometry 한 개를 공유하는 경우</strong><a href="http://www.diebuster.com/lab/memory0/" target="_blank"><img src="https://lh6.googleusercontent.com/-mum22vTFhjo/Tp2XeiBmsJI/AAAAAAAAAO8/LeNMuigOnhs/s400/Screenshot_1.jpg" alt="" /></a></p>
<p><strong>매 번 새로운 Geometry를 upload 하는 경우</strong><a href="http://www.diebuster.com/lab/memory1/" target="_blank"><img src="https://lh3.googleusercontent.com/-aAbC4v2pbN0/Tp2Xeo3vZ3I/AAAAAAAAAPM/Wr2_Sr7zNlM/s400/Screenshot_2.jpg" alt="" /></a></p>
<h4>복합 Material 실험</h4>
<p>얼터8이 A3D로부터 복잡한 재질 설정이 넘어온 경우 StandardMaterial 에 의존하여 처리하게 되는데 보다 깊고 자세한 부분을 탐구하기 위해 맥스의 설정을 여러 가지로 바꿔보며 연동을 테스트하던 중간 결과물입니다. 아래 결과물은 diffuse, normal, specular 등의 복합적인 텍스쳐가 적용되어있는 상태로 마우스로 이리저리 회전시킬 수 있습니다.<br />
<a href="http://www.diebuster.com/lab/standard0/" target="_blank"><img src="https://lh6.googleusercontent.com/-TE9sCChLUMY/Tp2XfQRraPI/AAAAAAAAAPU/Z3EOh6qUbw0/s576/Screenshot_4.jpg" alt="" /></a></p>
<h4>행렬지원함수 테스트</h4>
<p>얼터8은 비참하리 만큼 지원되는 행렬변환 함수가 없습니다. 따라서 bs11이 직접 추가적인 기능을 제공합니다. 이 테스트에서는 다양한 행렬변환 기능이 제대로 작동하는가를 확인할 수 있습니다. 좌상단의 select a Box를 한 경우는 큐브에 대해 파라메터가 적용되고 체크를 풀면 구에 적용됩니다.<br />
<a href="http://www.diebuster.com/lab/matrix0/" target="_blank"><img src="https://lh3.googleusercontent.com/-kQbnJYeT0gI/Tp2Xe7XiZ0I/AAAAAAAAAPA/Bo3AtgP_G4g/s550/Screenshot_3.jpg" alt="" /></a></p>
<h4>결론</h4>
<p>걍 지루함을 달래기 위해 원래 공개하면 안되는 개발 중의 여러 샘플을 올려봤습니다. 현재 bs11을 제작하는 코어멤버는 저 혼자가 아닙니다. 진우와 용호가 물심양면으로 돕고 실제 실험과 코딩도 같이 하고 있습니다. 앞으로도 이론적인 설명은 이어지겠지만 지루하지 않게 중간중간 결과물을 발표하도록 노력하겠습니다 ^^</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/486/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Alternativa3D 8.x의 구조와 bs11 #2</title>
		<link>http://www.diebuster.com/flash/474</link>
		<comments>http://www.diebuster.com/flash/474#comments</comments>
		<pubDate>Thu, 13 Oct 2011 14:25:59 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[bs11]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=474</guid>
		<description><![CDATA[저 번 글에 이어 이번에도 보다 깊은 곳까지 얼터8의 구조를 분석하고 bs11이 어떻게 대응하는지 설명하겠습니다. 과연 Context3D를 어떻게 이용하고 있을까? 저번 시간에 이어 아주 약간만 더 들어가보겠습니다. gpu파이프라인은 하드웨어적으로 정점버퍼와 상수버퍼, 쉐이더 버퍼로 추상화 되어있습니다. 원래 더 많은 것들이 있지만 플래시11이 추상화시킨 게 현재로서는 이 정도 입니다. 정점버퍼에 열심히 데이터를 밀어 넣는 경우 쉐이더는 정점버퍼를 [...]]]></description>
			<content:encoded><![CDATA[<p>저 번 글에 이어 이번에도 보다 깊은 곳까지 얼터8의 구조를 분석하고 bs11이 어떻게 대응하는지 설명하겠습니다.</p>
<h4>과연 Context3D를 어떻게 이용하고 있을까?</h4>
<p>저번 시간에 이어 아주 약간만 더 들어가보겠습니다.</p>
<p>gpu파이프라인은 하드웨어적으로 정점버퍼와 상수버퍼, 쉐이더 버퍼로 추상화 되어있습니다. 원래 더 많은 것들이 있지만 플래시11이 추상화시킨 게 현재로서는 이 정도 입니다.</p>
<p>정점버퍼에 열심히 데이터를 밀어 넣는 경우 쉐이더는 정점버퍼를 차례로 읽어 필요한 처리를 하게 됩니다. 예를 들어 큐브 10개가 있는 경우 각 큐브에 적당한 위치와 회전을 주고 변화를 반영시킨 큐브의 정점리스트 10개를 전부 정점버퍼에 넣으면 화면을 그릴 수 있습니다. 즉 원하는 화면은 정점버퍼에 삼각형 정보로 밀어 넣으면 된다는 거죠. 문제점은 다른 곳에 있습니다.</p>
<p>원래 3D의 정석적인 방법은 두 개의 렌더링 버퍼를 이용하는 더블 버퍼링입니다. 더블 버퍼링 자체는 플래시11도 지원하지만 DX나 OpenGL에서는 gpu를 사용하는 행렬변환 함수가 제공되므로 정점변환을 직접 호스트코드에서 처리한 뒤 후면 정점버퍼에 변환이 완료된 정점을 업로드 하여 전면 버퍼와 교체하는 게 일반적입니다. 하지만 플래시11의 context3D는 as3가 사용할 별도의 gpu함수를 제공하지도 않고 쉐이더의 결과를 반환하지도 않습니다. 따라서 정점리스트의 행렬변환을 as3내장객체인 Matrix3D 등으로 수행하게 되므로 매우 느려지게 됩니다(어도비에서 아예 Matrix3D를 아예 gpu를 타게 바꾸던가..어쩌라고=.=)<br />
<span id="more-474"></span><br />
그래서 꽁수가 등장하기 시작하는데 as3코드에서 행렬 변환하는 것이 손해이기 때문에 상수버퍼를 사용하게 됩니다. 물론 상수버퍼는 쓸모가 많지만 as3입장에선 절실한 거죠. 상수버퍼란 쉐이더가 실행 중에 얻을 수 있는 이미 공급된 데이터입니다. 원래 쉐이더는 정점버퍼의 정점데이터만 한 번에 하나씩 받을 수 있습니다. 하지만 상수버퍼의 내용은 쉐이더가 전부 알고 있습니다. 이런 점을 이용하면 기본적인 행렬변환도 전부 쉐이더에게 넘길 수 있습니다. 즉 상수버퍼에 원하는 행렬 변환 데이터를 넘겨주고 이를 바탕으로 정점에 대한 행렬 변환을 쉐이더가 하게 시켜 결과적으로 gpu가 변환을 하게 하는 거죠.</p>
<p>위와 같은 방식에서는 상수버퍼에 정점에 대한 행렬변환 정보를 기술해 줍니다. x, y, z, rotation, scale 등의 정보입니다. 헌데 이 데이터도 생각해보면 만만치 않습니다. 정점 하나에 행렬변환 정보 하나가 매칭될 수준이라면 상수버퍼의 데이터는 굉장히 방대해집니다. 따라서 기하구조가 변하지 않는 일종의 강체라면, 강체 당 하나의 행렬 변환 데이터를 보내는 식으로 데이터를 줄일 수 있습니다. 또한 이런 방법을 쓰면 draw콜에서도 이득이 생기는데 기하구조가 같은 경우 정점버퍼에는 기하구조를 하나만 넣어두고 상수버퍼에서 여러 개의 인스턴스 행렬변환 정보만 보내는 방식으로 처리하여 gpu메모리를 크게 절약할 수 있습니다.</p>
<p>플래시11의 3D는 as3처럼 꽁수와 편법이 난무하는 세계인거죠.</p>
<h4>얼터8은?</h4>
<p>얼터8은 플래시11 초창기부터 개발해 온 framework답게 플래시11의 편법을 모두 반영하고 있습니다. 예를 들어 큐브를 여러 개 생성한다고 하면 큐브 당 모든 기하정보를 전부 정점 버퍼에 올리지 않고 오직 하나의 기하구조만 올린 채 공유할 수 있습니다. 이게 가능한 이유는 앞 서 말씀 드린 대로 행렬변환 정보를 상수버퍼에 쓰기 때문이죠. 이를 코드로 표현하면 아래와 같습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var box1:Box, box2:Mesh, i:int, j:int, surface:Surface;

// box1을 먼저 만든다.
box1 = new Box;

// 1. box2를 빈 메쉬로 만든다.
box2 = new Mesh;

// 2. box1의 기하정보를 그대로 참조하자!
box2.geometry = box.geometry;

// 3.box1의 서페이스 정보를 복사하자!
for( i = 0, j = box1.numSurface ; i &amp;lt; j ; ++i ){
	surface = box1.getSurface( i );
	box2.addSurface( null, surface.indexStart, surface.numTriangle );
}
</pre>
<p>다른 메쉬의 기하정보를 가져오려면 귀찮게도 항상 저 3단계를 밟아야 합니다. 이렇게 되면 내부적으로 box1과 box2는 같은 기하정보(geometry)를 공유하게 됩니다. 따라서 다음과 같이 box1의 geometry만 업로드하면 box1, box2를 모두 화면에 표시할 수 있게 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
box1.geometry.upload( stage3Ds[0].context3D );
</pre>
<p>저번 글에서 살짝 언급했습니다만 더 정확하게 얼터8에서 메시의 의미를 짚어보면 다음의 세 가지로 요약됩니다.</p>
<ol>
<li>Geometry 객체를 참조로 잡는 컨테이너</li>
<li>Geometry내의 정점에 적용될 행렬 변환 데이터를 저장해두는 저장소</li>
<li>텍스쳐매핑과 연결해주는 추상 인터페이스 인 서페이스(Surface)의 저장소</li>
</ol>
<p>(음 더 쉽게 설명을 못하겠네요 ^^; 걍 여러 번 읽으시면 알게 되실 듯)</p>
<p>실제 무심하게 Box인스턴스를 양산하지 말고 위와 같은 방법으로 생성하면 화면에 동일한 수의 큐브를 표현해도 gpu메모리 사용은 크게 줄어듭니다.</p>
<h4>bs11은?</h4>
<p>일단 정점으로부터가 아니라 Geometry가 만들어진 객체를 생성하는 경우를 생각해보면 간단히 두 가지 경우 밖에 없습니다. 하나는 기본 모델(Primitive)로 부터 생성했거나 아니면 외부 모델을 로딩한 거죠. 먼저 기본 모델을 생성하는 경우 bs11은 내부에 각 기본 모델의 유일한 Geometry를 키와 함께 저장해두고 이를 이용해 위와 같은 비슷한 형태로 공유하는 메시를 생성해 줍니다.</p>
<p>하지만 더욱 강력한 기능은 외부모델 로딩 시에 일어납니다. 맥스 등에서 각 객체의 이름을 기술할 때 미리 bs11과 약속한 이름으로 기술하면 지오메트리를 그룹화 지어 내부적으로 복제를 해주는 방식입니다. 예를 들어 맥스에서 동일한 큐브를 5개 만들어 각각 scale과 rotation, position 등을 바꿔 장면에 배치한 경우를 생각해보죠. 그럼 그 5개 큐브에 이름을 맥스에서 다음과 같이 부여합니다.</p>
<p><b>cube1@geo=box1<br />
cube2@geo=box1<br />
cube3@geo=box1<br />
cube4@geo=box1<br />
cube5@geo=box1</b></p>
<p>그럼 bs11은 A3D등으로 모델을 로딩한 뒤 각 매쉬의 이름을 다음과 같은 규칙으로 분리해냅니다.</p>
<p><b>이름@속성1=값1@속성2=값2@속성3=값3&#8230;..</b></p>
<p>위에 등장한 속성 명은 geo라는 것인데 이것은 해당 메쉬가 특정 기하구조를 공유할 때 사용하는 속성입니다(더 많은 속성은 나중에 ^^) 따라서 메쉬의 이름으로부터 cube1~5가 동일한 box1 기하구조를 사용한다는 사실을 알아냅니다.</p>
<p>그 이후 과정은 앞 서 설명했던 기하구조 공유 형태로 처리하게 됩니다. 따라서 맥스에서 로딩한 모델도 단일 기하구조에 대한 참조를 실현하게 되는 거죠.</p>
<p>이 혜택은 매우 큰 데 제법 정교한 비행기 모델을 여러 개 장면에 배치해도 기하구조를 하나만 gpu에 업로드해주는 강력한 bs11의 기능입니다.</p>
<h4>upload와 dispose관리</h4>
<p>내부적으로 독립적인 기하구조 1개만 잡아 두고 참조를 공유하도록 bs11이 디자인되어있기 때문에 upload와 dispose를 자동으로 할 수 있는 기반이 됩니다.</p>
<p>기하구조를 어떤 메쉬가 인출해갈 때마다 인출카운터를 하나 올리고 반대로 반납할 때마다 하나를 줄이면 흔히 알려진 retain, release를 실현할 수 있습니다. 최초 0에서 1이 되는 순간 upload하고 다시 0으로 돌아오는 순간 dispose하는 것으로 화면에 있는 것만 upload하도록 관리할 수 있게 됩니다.<br />
호스트코드를 작성할 때 전혀 신경쓰지 않아도 장면에 관련된 기하구조만 업로드되고 완전히 제거되면 다시 메모리에서 자동으로 제거됩니다.</p>
<h4>결론</h4>
<p>일단 위에 글을 적으면서 설명이 생략된 내용이 바로 텍스쳐 매핑과 서페이스에 대한 내용입니다. 제가 아직 글에 적은 적이 없어 원래 아시는 분이 아니면 현재 저 부분이 뻥하고 이빨빠져있어 난감하실거라 생각합니다. 일단 지오메트리를 얼터가 어떻게 쓰는지를 전부 다룬 뒤 텍스쳐를 다룰 생각이라 그렇습니다. 서페이스와 텍스쳐는 이해가 잘 안가도 좀 기다려주세요 ^^;</p>
<p>bs11의 기본적인 기하시스템은 이 정도로 정리하고 다음에는 텍스쳐관리를 알아보겠습니다.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/474/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Alternativa3D 8.x의 구조와 bs11 #1</title>
		<link>http://www.diebuster.com/flash/471</link>
		<comments>http://www.diebuster.com/flash/471#comments</comments>
		<pubDate>Wed, 12 Oct 2011 04:15:14 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[bs11]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=471</guid>
		<description><![CDATA[bs11의 기저에는 얼터8이 깔려있습니다. 따라서 얼터8의 기초적인 구성이 어떻게 되어있는지 살펴보는 것은 반대로 bs11이 왜 그렇게 작동하는지를 이해할 수 있는 토대가 됩니다. 이번 포스트에서는 Geometry 시스템이 어떻게 얼터8에 구현되어있는지를 심층적으로 파악해보겠습니다. 3D를 구성하는 기하구조(Geometry) 다른 3Dframework처럼 얼터8도 GPU파이프라인에 기반한 기하클래스 군을 갖고 있습니다. 하지만 문제는 기존과는 완전히 다른  형태로 되어있다는 점입니다(얼터7과 비교해도 완전히 다릅니다) 현대 [...]]]></description>
			<content:encoded><![CDATA[<p>bs11의 기저에는 얼터8이 깔려있습니다. 따라서 얼터8의 기초적인 구성이 어떻게 되어있는지 살펴보는 것은 반대로 bs11이 왜 그렇게 작동하는지를 이해할 수 있는 토대가 됩니다. 이번 포스트에서는 Geometry 시스템이 어떻게 얼터8에 구현되어있는지를 심층적으로 파악해보겠습니다.</p>
<h4>3D를 구성하는 기하구조(Geometry)</h4>
<p>다른 3Dframework처럼 얼터8도 GPU파이프라인에 기반한 기하클래스 군을 갖고 있습니다. 하지만 문제는 기존과는 완전히 다른  형태로 되어있다는 점입니다(얼터7과 비교해도 완전히 다릅니다) 현대 서양 철학은 상대주의와 합리주의에 입각하기 때문에 OOP형태로 설계할 때 부속을 분해하여 설계합니다. 따라서 일반적인 기하구조를 포장한 클래스 구조는 대략 다음과 같은 형태가 됩니다.</p>
<ol>
<li>점을 Vertex를 통해 만든다.</li>
<li>면을 Face로 만들 때 위에 만든 Vertex을 이용한다.</li>
<li>매쉬를 Mesh로 만들 때 위에 만든 Face를 이용한다.</li>
</ol>
<p>이 얼마나 알기 쉬운 구조입니까 ^^;</p>
<p>점을 세 개 만들어서 면을 생성하고 그 면을 메쉬에 넣으면 끝난다 이거죠. 하지만 얼터8은 미묘한 구조로 되어있습니다. 일단 코드를 보겠습니다.</p>
<p><span id="more-471"></span></p>
<pre class="brush: as3; title: ; notranslate">
//지오메트리를 만들기 위해 먼저 명령배열을 만든다.
//점 세 개랑 이어서 UV좌표가 들어간다는 뜻
var  attributes:Array = [];
attributes[0] = VertexAttributes.POSITION;
attributes[1] = VertexAttributes.POSITION;
attributes[2] = VertexAttributes.POSITION;
attributes[3] = VertexAttributes.TEXCOORDS[0];
attributes[4] = VertexAttributes.TEXCOORDS[0];

//지오메트리를 생성하고 정점과 UV좌표, 삼각면 인덱스를 차례로 설정한다.
var geometry = new Geometry();
geometry.addVertexStream(attributes);
geometry.numVertices = 3;
geometry.setAttributeValues(
	VertexAttributes.POSITION,
	Vector.&amp;lt;Number&amp;gt;( [x1,y1,z1,x2,y2,z2,x3,y3,z3])
);
geometry.setAttributeValues(
	VertexAttributes.TEXCOORDS[0],
	Vector.&amp;lt;Number&amp;gt;( [u1,v1,u2,v2,u3,v3] )
);
geometry.indices =  Vector.&amp;lt;uint&amp;gt;( [0,1,2] );

//메쉬를 생성하여 위의 지오메트리를 할당하고 특정 인덱스로부터 지정된
//삼각면의 갯수로 서페이스를 생성한다. 재질은 서페이스 단위로 할당!
var mesh = new Mesh;
mesh.geometry = geometry;
mesh.addSurface( new FillMaterial( 0xff0000 ), 0, 1 );
</pre>
<p>이 구조를 보면 걍 지오메트리에 점이니 면이니 구분없이 다 때려박고 서페이스라는 건 재질만 구분해서 적용하려고 지오메트리를 임의로 구분짓는 구분자 같은 역할만 합니다. 뭐랄까 오히려 강연사의 하나는 전체, 전체는 하나 같은 느낌의 구조입니다. 다음의 코드를 통해 Surface를 얻어내도 Surface안에는 아무런 지오메트리가 포함되어있지 않습니다.</p>
<pre class="brush: as3; title: ; notranslate">
surface = mesh.getSurface( 0 );
//surface.....indexBegin, numTriangles 따위만 있고 지오메트리는 없음.
</pre>
<p>따라서 surface란 한마디로 Mesh내에 여러 개의 재질을 적용하기 위한 구분자 이상도 이하도 아닙니다. 따라서 mesh는 geometry를 설정하면 성립은 하지만 surface를 설정하지 않으면 재질을 적용할 수 없어 화면엔 표시할 수 없는 것이죠. 이것 참 적응하기 어려운 구조랍니다.</p>
<p>사실 Mesh자체도 따지고 보면 Geometry 컨테이너에 불과하므로 하나의 메쉬에서 지오메트리와 서페이스를 빼내서 보관하고 있으면 메쉬를 양산하거나 병합하는 건 큰 일이 아닙니다.</p>
<p>사실 이런 구조를 만든 건 러시아식 사고방식에서 나온 게 아니라 context3D에 업로드하기 편한 구조를 만들기 위해서였겠죠(얼터7은 이렇게 되어있지 않고 맨 처음 소개한 구조로 되어있습니다)</p>
<h4>Geometry간의 병합</h4>
<p>수동으로 Geometry 두 개를 병합한다고 해보죠. 모든 정점이 완전히 분리된 두 개의 Geometry라면 그저 VertexStream과 AttributeValues를 적당히 버무려주면 됩니다. 하지만 절대로 용이하지 않습니다.</p>
<ol>
<li>numVertexStreams로 명령의 갯수를 일일이 파악해서 for로 배열을 재생성 해야 하고,</li>
<li>getAttributeValues()를 통해 하나하나 정점과 UV좌표수치를 뽑아서</li>
<li>각각의 배열로 만들어냅니다.</li>
<li>그걸 두 개의 Geomerty마다 한 뒤 생성된 배열 두 쌍을 병합하여 새로운 Geometry에 적용합니다.</li>
</ol>
<p>아, 도저히 귀찮아서 샘플코드를 작성할 수가 없습니다(나중에 용호 시켜서 지오메트리 병합 메서드 하나 짜라고 해야지..) 게다가 저렇게 병합하면 겹쳐지는 정점을 자동으로 정렬하고 삼각형 인덱스를 재처리하는 작업도 안됩니다. 참고로 away의 경우는 여기까지를 한 방에 처리하는 메서드를 제공하고 있습니다. 여튼 얼터8에서 기하구조 두 개를 병합하는 건 귀찮은 작업입니다.</p>
<h4>Geometry의 upload</h4>
<p>실제 기저에 있는 context3D에 기하정보를 업로드하기 위한 추상인터페이스가 바로 얼터8에서 제공하는 upload라는 메서드입니다. 이 메서드는 Resource를 상속한 클래스는 전부 갖고 있는데 지오매트리와 재질 계열의 클래스가 이에 해당됩니다. 따라서 특정 지오메트리를 직접 context3D에 업로드하고 싶다면 다음과 같이 하면 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
mesh.geometry.upload( context3D );
</pre>
<p>하지만 mesh는 Object3D를 상속받았고 Object3D는 내부에 mesh를 비롯한 다양한 객체를 자식으로 갖고 있습니다. 이 중 geometry속성이 있는 모든 자식들을 순회하여 컴포지트 패턴형태로 지오메트리를 수집해 벡터로 반환하는 형태는 다음과 같습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var result:Vector.&amp;lt;Resource&amp;gt; = container.getResource( true, Geometry );
</pre>
<p>저렇게 하면 container안에 있는 모든 지오메트리를 수집하여 벡터로 반환하므로 다음과 같은 루프를 통해 업로드 되었는지 먼저 체크한 뒤 업로드를 하면 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
for each( var resource in container.getResource( true, Geometry ) ){
	resource.isUploaded ? null : resource.upload( context3D );
}
</pre>
<p>사실 mesh의 경우 자식을 갖지 않기 때문에 구지 getResource를 해서 벡터를 넘겨 받은 뒤 벡터의 요소를 꺼내 upload하는 건 뻘 짓입니다. 즉 아래의 예에서 두 번째 방식이 정답인거죠.</p>
<pre class="brush: as3; title: ; notranslate">
var mesh:Mesh = new Mesh;
...

//뭐하러 괜시리 벡터만들고 루프도냐..
for each( var resource in mesh.getResource( false, Geometry ) ){
	resource.isUploaded ? null : resource.upload( context3D );
}

//그냥 upload해버려
var resource = mesh.geometry;
resource.isUploaded ? null : resource.upload( context3D );
</pre>
<h4>결론</h4>
<p>context3D에 최적화 되어있는 얼터8의 Geometry를 살펴봤습니다. 참 유틸메서드 하나 지원안해주는 귀찮아쟁이 라이브러리입니다.</p>
<p>다음 글에서는 bs11이 여기에 어떻게 대처하는지를 적어보겠습니다.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/471/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>bs11소개(플래시11용 3D Framework)</title>
		<link>http://www.diebuster.com/flash/453</link>
		<comments>http://www.diebuster.com/flash/453#comments</comments>
		<pubDate>Sun, 09 Oct 2011 09:37:11 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[bs11]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=453</guid>
		<description><![CDATA[플래시11 3D Framework 2011년 10월에 발표된 플래시11은 GPU를 활용한 3D 디스플레이가 가장 큰 특징으로, 종전에 표현할 수 없던 화려한 그래픽 경험을 제공합니다. 하지만 이면에는 복잡한 프로그래밍적 문제가 산적해 있습니다. 보통의 개발자가 이런 벽을 뚫고 플래시11의 기능을 활용하기는 매우 어렵습니다. 또한 장벽을 넘어도 복잡성과 개발 분량이 기존에 비해 월등해 생산성이 크게 낮아집니다. 따라서 그 어느 때보다 [...]]]></description>
			<content:encoded><![CDATA[<h4>플래시11 3D Framework</h4>
<p>2011년 10월에 발표된 플래시11은 GPU를 활용한 3D 디스플레이가 가장 큰 특징으로, 종전에 표현할 수 없던 화려한 그래픽 경험을 제공합니다. 하지만 이면에는 복잡한 프로그래밍적 문제가 산적해 있습니다. 보통의 개발자가 이런 벽을 뚫고 플래시11의 기능을 활용하기는 매우 어렵습니다. 또한 장벽을 넘어도 복잡성과 개발 분량이 기존에 비해 월등해 생산성이 크게 낮아집니다.</p>
<p>따라서 그 어느 때보다 효과적인 외부 툴 연동을 필요합니다. 많은 부분을 외부의 저작 툴로 해결하고 프로그래밍은 필요한 기능만 추가하는 편이 적정한 제약 범위에서 만들 수 있게 해줍니다.</p>
<p>현재 플래시11을 지원하는 3D framework은 많은 종류가 있습니다. 각 특장점을 비교하는 것은 무리라 간단히 제 나름대로의 생각을 적어 봅니다.</p>
<p>일단 유, 무료로 구분할 수 있습니다. Flare3D, Unreal, Unity3D 등은 유료입니다. 유료는 지속적인 업데이트와 전용 개발 환경을 제공해주는 경우가 많습니다. 단점은 돈 내야 한다는 것 외에도 특정 회사의 개발 툴과 환경에 종속적이므로 향후 다른 시도를 할 때 전환 비용이 크게 들어갑니다. 이에 비해 무료는 지원, 책임감, 안정성 등이 약하고 지원 툴도 없는 경우가 많습니다. 하지만 돈이 안 들고 마음대로 소스를 뜯어보며 연구할 수 있는 장점이 있습니다.</p>
<p><span id="more-453"></span></p>
<h4>Alternative3D 8</h4>
<p>중간 형태의 라이브러리도 등장하고 있습니다. 무료지만 기업의 지원을 받고 있던가, 기업이 출자하여 개발하던 솔루션이 무료로 커뮤니티에 이양되는 경우입니다. 얼터너티바 3D 8(Alternative3D 8.x &#8211; 이하 <strong>얼터8</strong>)는 매우 특이한 케이스로 유료 라이브러리로 출발했지만 후에 기업의 지원을 받고 무료로 전환된 경우입니다. 저는 이런 점에서 얼터8을 높게 평가하고 있습니다. 신속한 환경의 변화에 대한 대응할 자금력도 확보되어 안정성 있게 유지할 가능성이 높기 때문이죠.</p>
<h4></h4>
<p>헌데 얼터8은 framework으로써 장단점이 미묘합니다. 안 좋은 면을 보면 단점이 너무 많고 동시에 좋은 면을 들여다 보면 장점도 많습니다. 쨌든 제가 느낀 얼터8의 소감을 간단히 정리하면 away4.x의 3배쯤 불친절하고, 1/3정도의 기능만 제공하며 2배쯤 코딩할 게 많다는 것입니다. 나쁜 면만 말하면 그렇고, 분명 좀 더 away4보다 빠르고 무엇보다 맥스(3D Max)의 연동에서 10배쯤 좋았습니다. 어쩌면 얼터8에 별 기능이 없는 이유는 맥스에서 대부분의 작업을 처리하고 읽어드려 사용하라는 의미일지도 모릅니다.</p>
<p>결론적으로 away4는 프로그래밍 인터페이스를 풍부하게 제공하는 반면 얼터8는 맥스와 연동을 고도로 지원하고 있습니다. 따라서 코드를 이용해 많은걸 만들고 싶다면 분명 얼터8 보다 away4를 추천합니다. 하지만 본격적인 게임이나 애플리케이션에서 코딩으로 창조하는 결과물은 한계가 분명합니다. 난해하고 우연적인 감각의 산물이 대부분입니다. 기획의도가 명확한 창조의 세계를 정교하게 전달하기 위해서는 맥스와 같은 저작도구의 도움을 받아야 하고 그러한 면에서 오히려 얼터8이 더욱 상용 솔루션을 만들기에 최적화 되어있는 셈입니다.</p>
<p>여담이지만 이러한 점 때문에 얼터8의 경우 away4보다 사용인구가 적을 수 밖에 없습니다. away4는 기존 플래시 개발자가 손쉽게 접근할 수 있는 API를 제공하는데 비해 얼터8는 맥스를 공부하길 훨씬 강력하게 요구하기 때문입니다.</p>
<h4>맥스와의 연동</h4>
<p>away4는 맥스 작업물을 매우 호환성이 나쁜 dae를 이용해 읽어 들입니다. 현재 away4와 완벽하게 연동되는 최신 버전의 맥스나 마야의 dae 플러그인은 없다고 봐도 무방합니다. 또한 dae를 만들어냈다고 해도 기본적으로 텍스트에 xml이기 때문에 파일용량도 크고 파싱에도 시간이 엄청나게 들어갑니다(away4는 이러한 이유로 dae파서가 함수로 호출되지 않고 이벤트로 완료를 보고하고 있는 지경입니다)</p>
<p>하지만 얼터8는 이런 점에서 완전히 다릅니다.</p>
<p>맥스2010과 2011에 맞는 전용 플러그인을 제공하고 있으며, 이 전용 플러그인을 이용해 A3D라는 전용 형식을 출력합니다. A3D는 애니메이션이나 카메라, 조명, 다양한 재질 정보를 전부 포함할 뿐만 아니라 이를 완벽하게 얼터8 클래스와 매핑할 수 있도록 설계된 이진 형식입니다.</p>
<p>따라서 매우 작은 용량으로 만들어질 뿐 아니라 파싱도 ByteArray를 읽어 들여 할당하는 방식이라 dae와 비교할 바가 아닙니다. 작은 모델 몇 개라면 몰라도 장대한 화면의 경우 100배 이상의 차이가 쉽게 발생합니다.</p>
<h4>얼터8을 사용하는 데 문제점</h4>
<p>그럼에도 불구하고 여전히 얼터8을 쓰기는 쉽지 않습니다. 얼터8이 쓰기 어려운 건 어찌 보면 당연한데, 기본적인 세 가지 장벽이 있습니다.</p>
<ol>
<li>플래시11의 3D환경을 이해하고 초기화할 수 있어야 한다.</li>
<li>3D를 이루는 기본 요소에 대한 이해와 이를 활용하는 수학적인 지식이 필요하다.</li>
<li>얼터8 API와 기능, 제약, 작동 방식을 익혀야 한다.</li>
</ol>
<p>당연하게도 얼터8은 플래시11의 3D기능을 기반으로 제작 되어 있습니다. 따라서 일부는 플래시11의 기능을 이해해야 얼터8도 사용할 수 있습니다. 하지만 이 부분은 기존 플래시10까지 없던 부분이라 완전히 새로운 영역입니다. 일단 이 점이 최초에 가로막는 장벽이 됩니다.</p>
<p>또한 얼터8은 3D를 만들어냅니다. 얼터8, 플래시11과 별도로 3D를 이해해야 합니다. 우리가 기존 2D에서 벡터와 비트맵의 차이를 알고 xy좌표계나 베지어 곡선을 알고 있는 것처럼 3D도 3D를 구성하는 기본 요소가 있습니다. 점, 삼각면, 서페이스, 매쉬, 컨테이너의 개념을 익히고 재질이 적용되는 구조와 조명과의 관계도 이해해야 합니다. 이건 오히려 첫 번째보다 더 큰 개념적인 장벽입니다.</p>
<p>이를 다 통과하시면 마지막으로 이러한 플래시11의 3D기능과 여러 개념을 어떻게 얼터8의 클래스로 표현되는지, 메서드를 사용하는지 배워야 합니다. 얼터8은 러시아에서 만들어진 라이브러리로 제법 독창적인 아키텍쳐를 갖고 있습니다. 암튼 공부해야겠지만 이것도 쉽지 않은 게 많은 문서가 러시아어로 되어있고, 도움말은 영어이긴 하지만 설명이나 예제가 부족하며, 시중에 역서는 물론 원서조차 출시되어있지 않은 상황입니다(2011.10월 현재)</p>
<p>이 세 가지 장벽을 다 깨부수고 전진하면 그 다음에 다시 다음의 세 가지 장벽이 나타납니다^^;</p>
<ol>
<li>얼터8은 3D관련 연산 메서드가 거의 제공되지 않으므로 수동으로 구현해야 한다.</li>
<li>물리엔진이 제공되지 않아 직접 다른 물리엔진과 연동을 구현해야 한다.</li>
<li>메모리를 통제하는 기능이 없으므로 복잡한 리소스의 업로드 관리를 직접 해야 한다.</li>
</ol>
<p>얼터8은 매우 가벼운 라이브러리입니다. 헌데 너무 가벼워서 제공되는 수학적 함수가 너무 부족합니다. 지역 좌표계 기준으로 회전이나 이동을 처리할 수 없고 특정 대상을 바라보는 등의 흔히 쓰는 기능에 대해서도 메서드를 제공하지 않습니다. 이러한 문제는 직접 구현한다고 완전히 해결되는 것은 아닙니다. 만약 얼터8 내부에 이것을 구현했다면 GPU를 응용할 수 있게 개선될 여지가 있지만(동적으로 AGAL을 수정하는 방식으로) 외부에서 이를 통제하면 CPU를 통한 연산을 해야 합니다. 하지만 현재로서는 그나마도 직접 구현해야 할 상황입니다.</p>
<p>얼터7계열에서는 살짝 지원되던 물리기능이 완전히 빠져있습니다. 또 다른 물리엔진을 가져와 붙여야 하는데 CPU를 사용하게 되므로 이도 성능에 나쁜 영향을 끼치게 됩니다.</p>
<p>메모리관리에 대한 얘기는 GPU에 로드 되는 쪽을 말하는 것입니다. 복잡한 화면은 다양한 재질용 이미지와 방대한 기하구조를 GPU에 올리게 되는데 너무 많이 올리면 작동하지 않으므로 적절하게 조정해야 합니다. 이러한 조정도 전부 개발자에게 맡기고 있으므로 효과적인 관리를 직접 해야 합니다.</p>
<h4>bs11의 목표</h4>
<p>bs11은 다음과 같은 목표를 갖고 개발되고 있습니다.</p>
<ol>
<li>고도의 생산성 &#8211; 매우 짧은 코드만으로도 효과적으로 개발할 수 있게 하면서 동시에 기능을 제약하지 않는 유연성을 동시에 제공하려 합니다.</li>
<li>직관적인 함수구조 &#8211; bs11내의 함수를 접두어 기반으로 정리하여 필요한 기능을 체계적으로 찾을 수 있게 도와줍니다.</li>
<li>효율적인 자원관리 &#8211; 그룹을 지원하여 3D리소스를 그룹별로 context3D에 올렸다, 내렸다 할 수 있는 기능을 지원합니다.</li>
<li>기타 기존의 DisplayObject, Event, Loader 등의 객체에 대한 추상층 제공 &#8211; 기존의 플래시 기능도 편리하게 사용할 수 있습니다.</li>
</ol>
<p>한 마디로 복잡하고 반복적인 일은 bs11에게 맡기자는 것입니다. 어떻게 그렇게 되는지는 수 차례에 걸친 포스팅으로 풀어가겠습니다.</p>
<p>현재 bs11은 구축단계로 기초 부분이 완성되었고 기능을 확장해가고 있습니다. 재밌는 개념을 도입하여 개발자의 실력과 무관하게 일정 수준의 품질이 유지되도록 노력하고 있습니다.</p>
<p>페이스북의 s49라는 스터디에서는 bs11을 활용한 다양한 3D 애플리케이션 제작도 10회에 걸쳐 진행 중입니다.</p>
<p>스터디나 bs11에 관심있는 분들은 제게 연락 주시면 답변해드리겠습니다.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/453/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>지연평가 활용하기 #2</title>
		<link>http://www.diebuster.com/flash/441</link>
		<comments>http://www.diebuster.com/flash/441#comments</comments>
		<pubDate>Sat, 24 Sep 2011 04:33:35 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[3 흐름제어]]></category>

		<guid isPermaLink="false">http://www.diebuster.com/flash/?p=441</guid>
		<description><![CDATA[전 포스팅에서 지연실행의 기초를 다뤘습니다. 지연평가활용하기 #1 이번 포스트에서는 지연실행의 꽃이라 할 수 있는 함수를 통한 지연실행에 대해 써 볼까 합니다. 제어형 언어에서 지연실행은 크게 두 가지로 나눌 수 있습니다. 첫 번째는 저번에 다뤘던 평가 지연이고 다른 하나는 이 번에 다루는 호출 지연입니다. 제어형 언어의 3대 요소는 문(statement), 식 (expression), 할당 (assignment) 입니다. 이 중에서 [...]]]></description>
			<content:encoded><![CDATA[<p>전 포스팅에서 지연실행의 기초를 다뤘습니다.</p>
<p><a href="http://www.diebuster.com/flash/177" target="_blank">지연평가활용하기 #1</a></p>
<p>이번 포스트에서는 지연실행의 꽃이라 할 수 있는 함수를 통한 지연실행에 대해 써 볼까 합니다.</p>
<p>제어형 언어에서 지연실행은 크게 두 가지로 나눌 수 있습니다. 첫 번째는 저번에 다뤘던 평가 지연이고 다른 하나는 이 번에 다루는 호출 지연입니다. 제어형 언어의 3대 요소는 문(statement), 식 (expression), 할당 (assignment) 입니다.</p>
<p>이 중에서 문과 식은 평가 지연을 일으킬 수 있습니다. 평가 지연이란 평가가 일어날 때까지 특정 명령을 실행하지 않고 대기 시켜 두는 것을 말합니다(구체적인 내용을 저 번에 다뤘습니다 ^^) 호출 지연이란 말 그대로 호출을 하는 시점까지 실행하지 않고 대기 시키는 기능입니다. 평가 지연은 평가 시점 자체를 조절할 수 없다는 점에서 일일 호프에 가는 길을 표시하기 위해 바닥에 붙여둔 화살표 판들이라 이해할 수 있습니다. 유도하여 그 일이 일어나게 미리 장치해두는 것이죠. 이건 마치 바둑이나 장기처럼 미리 일어날 일을 예측해서 포석을 깔아두는 방식이라 수가 깊어질 수록 통제가 제대로 되지 않습니다. 이 점이 평가 지연을 한 두 단계만 일어나게 끊어버리는 이유입니다. 가령 조건문을 두 번 이상 중첩하지 말라던가 하는 식입니다. 이에 비해 호출 지연은 피자가 먹고 싶을 때 전화를 걸어 주문하는 것과 마찬가지로 이해할 수 있습니다.<br />
모든 문제의 근원은 사람인데 사람에겐 평가 지연을 정교하게 배치하는 것보다 호출 지연을 사용하는 것이 훨씬 사고하기 편리합니다. 여담이지만. 평가 지연을 정교하게 깔아두는 방식의 프로그램이 바로 수학적 프로그램입니다. 수학이란 어쩔 때 보면 인간의 본성을 거스르고 있다는 생각도 가끔 합니다. 수학적 프로그래밍은 보통 순수한 함수형 언어에서 개발하는 방법입니다.</p>
<p><span id="more-441"></span></p>
<h4>호출 지연 사용하기</h4>
<p>호출 지연을 사용하려면 단순히 함수를 만들고 호출하는 것으로는 안됩니다. 그렇게 하면 함수의 호출이 실행흐름에 따라 자동으로 일어나기 때문에 원하는 시점에 호출할 수 없습니다. 따라서 호출할 함수 자체를 기억해뒀다가 원할 때 호출하는 방법을 사용해야 합니다.<br />
파워플의 제 포스트 내용 중에 함수가 어떻게 객체가 되는지를 개념적으로 다룬 글이 있습니다.</p>
<p><a href="http://powerfl.com/?p=2503" target="_blank">rest인자의 활용</a></p>
<p>이 글의 초반에 나오는 함수의 가상 객체 부분을 보시면 함수도 하나의 객체가 될 수 있다는 생각이 드실 겁니다. 요점은 as에서 함수는 일반적인 참조 객체로 변수에 저장할 수 있기 때문에 호출지연을 위해 그저 함수 자체를 변수에 저장해두면 된다는 점입니다.<br />
따라서 아래와 같이 함수를 변수에 담았다가 원할 때 호출할 수 있습니다.</p>
<pre class="brush: as3; title: ; notranslate">
function log():void{
	trace( 'test' );
}

var func:Function;
func = log;

func();
</pre>
<p>하지만 함수란 인자를 받을 수 있습니다. 인자를 받을 수 없는 함수는 기능이 제한되기 때문에 필요에 따라 너무 많은 함수를 만들어야 합니다. 하지만 인자는 함수를 호출할 때 전달되기 때문에 사실 함수에 넘겨질 인자는 함수를 저장하는 시점에 같이 저장해야만 나중에 원하는 결과를 얻을 수 있습니다. 이를 코드로 표현하면 아래와 같습니다.</p>
<pre class="brush: as3; title: ; notranslate">
function log( $msg:String ):void{
	trace( $msg );
}

var func:Function, msg:String;
func = log;
msg = 'test';

func( msg );
</pre>
<p>이러한 상황은 함수 한 개와 인자 한 개를 잡아둘 때는 괜찮지만 다양한 함수와 그 인자를 잡아두르면 처음부터 인자를 배열로 저장해두고 함수객체의 apply를 이용하는 편이 보다 일반화 할 수 있습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var func:Function, arg:Array;
func = log;
arg = ['test'];

func.apply( null, arg );
</pre>
<h4>defer와 invoker</h4>
<p>그럼 이상의 내용을 요약하여 하나의 클래스로 정의하고 이 이름을 defer(지연)이라고 부르겠습니다.</p>
<pre class="brush: as3; title: ; notranslate">
class Defer{

	private var _func:Function;
	private var _arg:Array;

	public function Defer( $func:Function, $arg:Array = null ){
		_func = $func;
		_arg = $arg.concat();
	}

	public function run():*{
		if( _arg ){
			return _func();
		}else{
			return _func.apply( null, _arg );
		}
	}
}
</pre>
<p>내용은 워낙 쉽습니다만 run에서 인자가 있냐 없냐로 apply를 사용할지 말지를 결정해 성능 상의 이득을 보게 해뒀습니다.</p>
<p>이제 호스트 코드를 다음과 같이 작성할 수 있습니다.</p>
<pre class="brush: as3; title: ; notranslate">
function log( $msg:String ):void{
	trace( $msg );
}

var defer:Defer;
defer = new Defer( log, ['test'] );
defer.run();
</pre>
<p>일단 defer를 맘대로 쓸 수 있으면 이를 묶어 커맨드 셋을 만들 수 있습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var greeting:Array;
greeting = [
	new Defer( log, ['안녕~'] ),
	new Defer( log, ['난 노노야.'] ),
	new Defer( log, ['바이바이'] )
];
</pre>
<p>그럼 커맨드 셋 단위로 실행시킬 인보커 함수를 하나 만들어야 합니다.</p>
<pre class="brush: as3; title: ; notranslate">
function invoker( $commans:Array ):void{
	var i:int, j:int;
	for( i = 0, j = $commands.length ; i &amp;lt; j ; ++i ){
		$commands.run();
	}
}
</pre>
<p>이젠 커맨드를 인보커 함수를 통해 언제라도 호출할 수 있게 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
invoke( greeting );
invoke( introduce );
</pre>
<p>추상화 단계를 거칠 수록 함수를 변수로 잡아두는 것은 자연스레 은닉되고 그러함 함수와 인자를 셋트로 묶어 다양한 의미를 부여할 수 있게 됩니다.</p>
<h4>이벤트 리스너로 사용하기</h4>
<p>이벤트에 등록되는 리스너는 조건이 단일한 하나의 인자인 Event를 받아들인다는 점입니다. 위에서 작성한 defer는 run이 호출될 때 어떠한 인자도 받지 않습니다. 그럼 defer의 run을 약간만 개조해서 일반적으로도 사용할 수 있고 이벤트 리스너로도 사용할 수 있게 개조해보죠.</p>
<pre class="brush: as3; title: ; notranslate">
class Defer{

	...

	public function run( ...arguments ):*{
		if( _arg ){
			return _func();
		}else{
			return _func.apply( null, _arg );
		}
	}

}
</pre>
<p>저렇게 하면 run이 인자를 자유롭게 받아들일 수 있으므로 이벤트 리스너로 사용할 수 있게 됩니다. 이제 여러 버튼에 클릭 이벤트를 걸 때 각각 다른 메세지를 출력하는 호스트코드를 간단히 작성할 수 있게 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
function log( $msg:String ):void{
	trace( $msg );
}

var defer0:Defer, defer1:Defer, defer2:Defer, defer3:Defer;
defer0 = new Defer( log, ['안녕'] );
defer1 = new Defer( log, ['잘자'] );
defer2 = new Defer( log, ['배고파'] );
defer3 = new Defer( log, ['밥먹자'] );

buttun0.addEventListener( 'click', defer0.run );
buttun1.addEventListener( 'click', defer1.run );
buttun2.addEventListener( 'click', defer2.run );
buttun3.addEventListener( 'click', defer3.run );
</pre>
<h4>인보커도 리스너로 사용하기</h4>
<p>위에서 인보커를 클래스로 정의하지 않고 함수로 정의했는데 이를 확장하여 클래스로 정의하면 인보커 자체도 이벤트의 리스너로 사용할 수 있게 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
class Invoker{

	private var _commands:Array;

	public function Invoker( $commands:Array ){
		_commands = $commands.concat();
	}

	public function run( ...arguments ):*{
		var i:int, j:int;
		for( i = 0, j = _commands.length ; i &amp;lt; j ; ++i ){
			_commands[i].run();
		}
	}
}
</pre>
<p>이제 defer 여러 개를 묶어 인보커에 전달하고 인보커를 리스너로 사용해 보죠.</p>
<pre class="brush: as3; title: ; notranslate">
function log( $msg:String ):void{
	trace( $msg );
}

var invoker:Invoker;
invoker = new Invoker( [
	new Defer( log, ['안녕'] ),
	new Defer( log, ['배고파'] ),
	new Defer( log, ['밥먹자'] ),
	new Defer( log, ['잘자'] )
] );

button.addEventListener( 'click', invoker.run );
</pre>
<h4>결론</h4>
<p>defer객체를 이용하면 손쉽게 커맨드패턴을 구현할 수 있을 뿐만 아니라 커스텀이벤트를 정의해야할 필요가 없어집니다. 함수 뿐 아니라 함수의 인자도 동시에 참조해둘 수 있는 구조를 이용해 인보커까지 확장하면 함수를 호출하는 행위 자체로 새로운 구조의 언어를 작성할 수 있습니다.</p>
<p>지연 호출은 매우 강력한 언어의 기능으로 이미 C의 함수포인터 등 전통적으로 내려오는 프로그래밍 기법입니다. 체계적이고 안전한 방법으로 응용하면 프로그램을 매우 유연하고 컴팩트하게 작성할 수 있습니다.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/441/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Bitmapdata를 관리하기</title>
		<link>http://www.diebuster.com/flash/181</link>
		<comments>http://www.diebuster.com/flash/181#comments</comments>
		<pubDate>Wed, 16 Feb 2011 18:04:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[2 픽셀데이터]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[플래시로 컨텐츠를 제작한 결과는 swf파일이 됩니다. swf파일은 간단한 형식의 바이너리 파일로 일명 abc포멧이라 불리는 규격으로 만들어집니다. 이 규격은 매우 간단하기 때문에 간단한 컨텐츠의 경우는 hex에디터에서 손으로 만들 수 있는 수준입니다(php의 ming이라던가 다른 여러가지 프로젝트들이 있습니다^^) 이 abc포멧을 분석하다보면 mp3나 jpg등이 거의 날로 swf에 포함되어 있다는 사실을 알 수 있습니다. 반대로 말하자면 mp3나 jpg만 최적화 해도 [...]]]></description>
			<content:encoded><![CDATA[<p>플래시로 컨텐츠를 제작한 결과는 swf파일이 됩니다. swf파일은 간단한 형식의 바이너리 파일로 일명 abc포멧이라 불리는 규격으로 만들어집니다. 이 규격은 매우 간단하기 때문에 간단한 컨텐츠의 경우는 hex에디터에서 손으로 만들 수 있는 수준입니다(php의 ming이라던가 다른 여러가지 프로젝트들이 있습니다^^)</p>
<p>이 abc포멧을 분석하다보면 mp3나 jpg등이 거의 날로 swf에 포함되어 있다는 사실을 알 수 있습니다. 반대로 말하자면 mp3나 jpg만 최적화 해도 swf의 용량을 크게 줄일 수 있다는 결론이 나옵니다.</p>
<p>하지만 현대의 컨텐츠는 무지막지한 이미지와 사운드를 요구하기 때문에 사실 자원에 대한 최적화가 그다지 의미없는 경우가 많습니다.</p>
<p>따라서 이미지 자체도 최적화하고 런타임에 필요한 자원을 로드하여 적시에 사용하는 방법을 병행하여 제한된 네트웍 속도 내에서 원할하게 작동하도록 설계하는 방식이 도입되고 있습니다. 최근 오픈된 많은 플래시 컨텐츠들은 프리로더가 없거나 최소로 작동하게 되어 있는데 바로 이러한 점층적 로딩을 반영한 것이라 할 수 있습니다.</p>
<p>진짜 문제는 여기서 부터인데 분산하여 로딩하는 기술은 더욱 대규모의 이미지 자원을 로딩하게 하는 결과를 만들어낸다는 것입니다.</p>
<p>따라서 이전까지는 gc가 작동하는 타이밍 정도에 이미지리소스가 메모리에서 해제되어도 괜찮았던 것이 현재는 더욱 빨리 메모리에서 효율적으로 제거하지 않으면 gc가 작동하시기 전에 avm2자체가 세상에서 하직하고 맙니다. 이는 브라우저 등에서 플로그인모드로 작동하는 경우 더욱 심각해집니다. 브라우저들은 플러그인에 대해 제한된 메모리와 cpu사용권을 주고 있기 때문이죠.</p>
<p><span id="more-181"></span></p>
<p>근데 이 이슈가 하필 이미지냐..<br />
이미지 한 장이 잡아먹는 메모리가 여타 다른 언어 요소가 사용하는 메모리양과 비교가 되지않을 정도로 크기 때문입니다. 이미지만 적시에 내려도 그럭저럭 위기는 없고 충분히 gc타이밍에 처리될 수준으로 내려갑니다. 업무용 플렉스어플이야 워낙 데이터로드가 많으니 별도라 쳐도 그외의 어플들은 제법 살만해지는 거죠</p>
<h4>애매한 dispose와 참조의 관계</h4>
<p>일단 아래와 같이 어떤 bitmapData를 로더로부터 얻었다고 합시다.</p>
<pre class="brush: as3; title: ; notranslate">
var b:bitmapdata = $e.target.content.bitmapData;
</pre>
<p>그럼 먼저 clone을 뜨고 원본을 dispose()시켜야 합니다. 이유는 그래야 loader객체와 그 자식인 content가 안전하게 gc대상이 되면서 향후 어플에서는 얻어온 비트맵을 자유롭게 사용할 수 있기 때문입니다. gc는 편리하지만 참조가 꼬리를 물지 않게 하려면 매우 주의깊게 객체를 관리해야 합니다. 코드로 보죠.</p>
<pre class="brush: as3; title: ; notranslate">
var _bitmap:BitmapData:

function load( $url:String, $complete:Function ):void{

	var loader:Loader = new Loader:

	loader.loadInfo.addEventListener( Event.COMPLETE, function( $e:Event ):void{

		//사본을 얻고
		_bitmap = $e.target.content.bitmapData.clone();

		//원본은 제거하고
		$e.target.content.bitmapData.dispose();

		//완료보고
		$complete( this );
	} );

	loader.load( new URLRequest( $url ) );
}
</pre>
<p>위의 코드에서 loader와 리스너가 모두 지역변수이고 bitmap을 얻은 후 참조를 유지하는 대상이 없기 때문에 안전하게 gc대상이 됩니다. 하지만 gc가 일어나기 전에 미리 dispose()를 통해 필요없는 비트맵을 제거합니다. 만약 위에서 clone()을 하여 _bitmap에 할당하지 않고 직접 할당하게 되면 참조관계가 꼬리를 물어 결국 loader까지도 전부 gc되지 않게 됩니다.</p>
<h4>dispose()</h4>
<p>하지만 위의 코드는 동적으로 로딩한 이미지를 얻는 과정에서 부산물인 loader를 성공적으로 객체참조 해지 했을 뿐 보다 근본적인 _bitmap의 강제 해지 시점을 해결한 건 아닙니다.</p>
<p>간단히 _bitmap.dispose() 할 시점을 생각해보면</p>
<p>1. 아무도 이 비트맵을 사용하지 않고<br />
2. 앞으로도 사용하지 않을 때</p>
<p>라고 할 수 있습니다.</p>
<p>근데 그게 언제인가요? ^^;</p>
<p>이 질문은 거의 gc를 제작하는 것만큼이나 어려운 질문입니다. 하지만 특수한 상황이니 범위를 한정지어 질문을 반대로 생각해봅시다.</p>
<h4>현재 사용 중인가를 판단하는 근거</h4>
<p>간단하게는 비트맵 사용 현황을 카운트해보면 알 수 있겠죠. 아이폰 덕분에 매우 유명해진 retain, release 개념은 사실 풀링의 기본 알고리즘입니다. 중앙에서 자원을 줄 때 retain을 통해 참조카운트를 하나 올리면 증가는 간단히 해결됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
class BitmapSRC{

	var _count:int;
	var _bitmap:BitmapData:

	public function BitmapSRC(){}

	public function load( $url:String, $complete:Function ):void{...}

	public function getBitmapData():BitmapData{
		//카운터를 증가시킴
		++_count;
		return _bitmap;
	}

}
</pre>
<p>그럼 카운트를 줄이는건 언제인가요?</p>
<p>물론 명시적으로 자원을 반환하는 경우가 있겠습니다. 이 경우는 release를 호출하면 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
class BitmapSRC{
	...
	public function release( $e:Event = null ):void{
		--count;
	}
}
</pre>
<p>하지만 자동으로 반환하는 경우도 생각해볼 수 있습니다. (귀찮기 때문에..^^)<br />
자동반환은 다양한 이슈가 있겠지만 디스플레이 오브젝트 기준이라면 가장 합리적인 것은 스테이지에서 내려갈 때라고 할 수 있죠(Event.REMOVE_FROM_STAGE)</p>
<pre class="brush: as3; title: ; notranslate">
//이거슨 호스트 코드!

var src:BitmapSRC = new BitmapSRC:

src.load( 'girl.jpg', complete );

function complete( $src:BitmapSRC ):void{
	var bitmap:Bitmap;

	//새비트맵에 데이터할당
	bitmap = new Bitmap( $src.getBitmapData() );

	//스테이지에서 제거될 때 자원참조 카운터도 줄인다.
	bitmap.addEventListener( Event.REMOVE_FROM_STAGE, $src.release );

	stage.addChild( bimap );

	//remove될 때 참조카운터 감소!
	stage.removeChild( bitmap );

}
</pre>
<p>위의 코드에서 remove가 된 후의 _count는 0이 됩니다.<br />
이 과정을 자동으로 처리하려면 역시 자원을 가져갈 때 인자로 자동인지 아닌지 선택할 수 있게 getBitmapData를 보완해주는 것이 좋겠습니다.</p>
<pre class="brush: as3; title: ; notranslate">
class BitmapSRC{
	...
	public function getBitmapData( $isAutoRelease:Boolean = false, $target:DisplayObject = null ):BitmapData{

		++_count;

		if( $isAutoRelease &amp;&amp; $target ){ //오토릴리즈에 타겟도 넘겨받았다면!

			//아까 호스트코드에서 하던 짓을 여기서 한다.
			$target.addEventListener( Event.REMOVE_FROM_STAGE, release );
		}

		return _bitmap;
	}
	public function release( $e:Event = null ):void{

		--count;

		if( $e ){ //이벤트에 의해 release가 온 경우에는

			//리스너를 해지해준다.
			$e.target.removeEvent( $e.type, arguments.callee );

		}
	}
}
</pre>
<p>제가 잘 쓰는 away의 경우는 컨테이너에서 제거될 때 다른 이벤트로 처리되므로 어떤 경우인가에 따라 자원을 자동해지 시키는 방법을 확장할 수 있도록 해야합니다.<br />
(이건 과제로 내기로 하고&#8230;^^)<br />
암튼 그래서 결국 카운트가 0인 자원은 사용 중이 아니라고 할 수 있겠죠.</p>
<h4>앞으로도 사용하지 않을 때는?</h4>
<p>그러니까 말이죠. 객체 참조 카운터야 어떻게든 관리를 하면 되는데 대체 앞으로도 사용하지 않을 것 같다라는 건 어떻게 아냐는거죠. </p>
<p>사실은 모릅니다 ^^; 갑자기 어떤 경우에 쓸지 모르는 가능성이야 항상 있는거 아니겠습니까.</p>
<p>하지만 무한히 낮은 가능성 때문에 메모리에서 해지하지 않고 있다면 그것 또한 웃기는 일입니다. 이러한 중간 타협점이 바로 GC라 할 수 있지만 GC알고리즘는 이미 일어난 일만 처리할 수 있도록 되어있지 미래를 예상하도록 되어있지는 않습니다.</p>
<p>본래 언어수준에서 바인딩된 GC알고리즘을 수행하는 쓰레드의 경우 메모리를 정적, 스택, 힙으로 나누고 정적영역과 스택에서 선언한 힙메모리 참조를 전부 뒤져서 실제 힙에 있는 메모리가 스택과 정적메모리에 매칭되는 참조가 없는 경우 해당 객체의 힙메모리를 해지하는 식으로 작동합니다(풀어쓰기 생략..=.=)</p>
<p>위의 비트맵자원의 경우 사실 _count가 0이 되면 어디에도 참조가 없다는 뜻이므로 사실 곧장 제거해도 무방합니다.<br />
단지 그 이후에 그것을 사용하려고할 때 없는 자원이 되어서 문제인거죠.</p>
<p>따라서 GC의 방식을 사용할 수는 없고 두가지 다른 대안을 마련할 수 있습니다.</p>
<p>1. 타이머를 통해 count가 0이 된 뒤 일정시간까지 계속 count가 0 이라면 해지한다.<br />
2. drain을 통해 일괄로 흡수한다.</p>
<p>drain도 아이폰 덕분에 많이 유명해졌습니다. pool을 정리하는 과정이라 보시면 되는데, 명시적으로 drain을 호출하면 자원을 무조건 초기화하면 됩니다. 즉 유저가 더 이상 이 자원은 사용하지 않겠다라고 선언한다는거죠. 그건 간단하니 구현도 쉽습니다.</p>
<pre class="brush: as3; title: ; notranslate">
class BitmapSRC{
	...
	public function drain():void{
		_count = 0;
		_bitmap.dispose();
	}
}
</pre>
<p>타이머 방식도 별거 아니니 쉽게 구현할 수 있습니다.</p>
<pre class="brush: as3; title: ; notranslate">
class BitmapSRC{
	...
	public function release( $e:Event = null ):void{

		--count;

		if( $e ){
			...
		}

		if( count === 0 ){ //count가 0이 되면

			//30초 후에 drain할지를 판단한다.
			setTimeout( isDrain, 30000 );

		}
	}

	public function isDrain():void{

		if( count === 0 ){ //30초가 지났는데도 여전히 count가 0이면 진짜로 drain하자!
			drain();
		}

	}
}
</pre>
<h4>결론</h4>
<p>견고한 이미지 자원 관리기는 이제 대용량 컨텐츠를 계속 올렸다 내렸다 하는 플래시 앱에게 필수적일 뿐만 아니라 반복적인 작업입니다.<br />
한번 쯤은 매우 신경써서 구현을 하셔야 한다고 생각합니다.<br />
이 포스트의 내용은 매우 기초적이므로 힌트를 얻어서 멋진 통합 관리자를 만드셨다면 꼭 댓글로 같이 공유해주시면 감사하겠습니다(그러면서 막상 제건 공개안하고 있다..ㅎㅎ)</p>
<h4>과제</h4>
<p>DisplayObject가 아닌 객체가 bitmapData를 가져가는 경우에도 autoRelease를 걸 수 있게 하려면 단순히 그 부분을 if로 분기해서는 다양한 상황에 적응할 수 없습니다. 어찌할까요?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/181/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Non blocking loop</title>
		<link>http://www.diebuster.com/flash/180</link>
		<comments>http://www.diebuster.com/flash/180#comments</comments>
		<pubDate>Tue, 08 Feb 2011 10:12:36 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[3 흐름제어]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Blocking 제어형 언어는 명령을 메모리에 적재하고 순차적으로 실행하기 때문에 사실 상 언제나 cpu를 blocking하고 있습니다. 일단 blocking이 뭔지 자세히 설명하자면 한 마디로 컴터가 그걸 실행하는 동안 먹통이 되는 상태를 말합니다. 비슷한 말로는 동기화(sync) 로직이라고도 합니다. 하지만 현대의 OS들은 시분할 방식으로 cpu 제어권을 강제 탈환할 수 있기 때문에 멀티태스킹환경에서는 blocking제어명령을 보내도 빠져나올 수 있습니다. 따라서 많은 [...]]]></description>
			<content:encoded><![CDATA[<h4>Blocking</h4>
<p>제어형 언어는 명령을 메모리에 적재하고 순차적으로 실행하기 때문에 사실 상 언제나 cpu를 blocking하고 있습니다.</p>
<p>일단 blocking이 뭔지 자세히 설명하자면 한 마디로 컴터가 그걸 실행하는 동안 먹통이 되는 상태를 말합니다. 비슷한 말로는 동기화(sync) 로직이라고도 합니다.</p>
<p>하지만 현대의 OS들은 시분할 방식으로 cpu 제어권을 강제 탈환할 수 있기 때문에 멀티태스킹환경에서는 blocking제어명령을 보내도 빠져나올 수 있습니다. 따라서 많은 프로그래밍기법에서 thread를 활용하여 blocking명령을 다수 처리하거나 관리하는 기법을 사용합니다.</p>
<p>싱글쓰레드 기반으로 구성된 avm2의 경우 강제적인 시분할 타이밍을 만들어뒀는데 그게 바로 enterFrame으로 대표되는 frame시스템입니다. frame은 마치 쓰레드처럼 주기적으로 반복되는 실행타이밍인데 os가 제어권을 갖고 있지 않고 avm2가 제어권을 갖고 있기 때문에 한계가 명확합니다.</p>
<p>즉 한 프레임에서 긴 시간을 점유하는 blocking명령을 내리면 다음 프레임으로 강제로 넘길 수 없고 blocking이 풀릴 때까지 기다려야 한다는 거죠.</p>
<p>따라서 avm2의 흐름대로 프로그래밍 효과를 최대한 내고 싶다면 한 프레임이 실행되는 주기에 최대한 blocking 시간을 짧게 만들 필요가 있습니다.</p>
<p><span id="more-180"></span></p>
<p>어려운 용어는 이쯤하고 실제 blocking코드를 간단히 보죠.</p>
<pre class="brush: as3; title: ; notranslate">
for( i = 0 ; i &amp;lt; 999999 ; ++i ){
	Math.random();
}
</pre>
<p>컴터 성능에 따라 다소간 차이는 있겠지만 이거 엄청나게 오랜 시간동안 컴터를 먹통으로 만듭니다.</p>
<p>이러한 명령이 실행되면 실행이 완료될 때까지 다음 프레임으로 넘어가지 못합니다. 플래시는 화면업데이트나 이벤트처리, 다른 로직의 처리 등을 전부 프레임주기로 갱신하기 때문에 다음 프레임으로 넘어가지 못하면 플래시 그 자체가 먹통이 됩니다.</p>
<p>게다가 script실행 환경 상 타임아웃제한이 있습니다. 위의 코드를 실행하여 30초가 넘어가면 더 이상 실행되지 못하고 다운됩니다. 따라서 blocking을 분산하는 기술은 대규모 프로그래밍의 필수적인 요소입니다.</p>
<p>제어형 언어에서 blocking을 일으키는 경우는 다양하게 있지만 이번 포스트에서는 loop만 다루도록 하겠습니다.</p>
<h4>enterFrame</h4>
<p>일단 위의 blocking로직을 non blocking으로 만들려면 잘게 쪼개 enterFrame에 태워야합니다. enterFrame을 사용하려면 최소한의 displayObject가 필요합니다. 가벼운 객체인 shape를 이용해 enterFrame을 준비하겠습니다.</p>
<pre class="brush: as3; title: ; notranslate">
var looper:Shape = new Shape;
</pre>
<p>다음은 전역 카운터가 필요합니다. 위의 for에서 999999에 해당되는 거라 할 수 있죠.</p>
<pre class="brush: as3; title: ; notranslate">
var counter:int;
</pre>
<p>또한 한 프레임에서 루프를 돌 제한도 알아야합니다. 즉 999999를 한프레임에 얼마나 쪼개서 돌릴 것인가라는거죠. 예를들어 300번씩만 돌린다면 999999/300 = 3334 프레임으로 돌아야 전체 루프가 완성될 것입니다. 초당 60프레임으로 돌린다면 프레임당 딜레이가 없다고 가정할 때 56초가 걸리게 됩니다.</p>
<pre class="brush: as3; title: ; notranslate">
var frameLimit:int;
</pre>
<p>다음은 루프의 몸체에 들어갈 함수와 그 함수에 전달한 인자입니다만, 보통 루프몸체의 로직은 루프 카운터를 활용하는 경우가 대부분 이므로 counter도 알 수 있게 해줘야 합니다.</p>
<pre class="brush: as3; title: ; notranslate">
var runner:Function;
var param:Array;
</pre>
<p>이제 조각이 다 모였으니 간단한 클래스로 일반화 시킬 수 있습니다.</p>
<pre class="brush: as3; title: ; notranslate">
class nonBlockingLoop{

	var looper:Shape = new Shape;

	public function nonBlockingLoop(){}

	public function loop( $counter:int, $limit:int, $runner:Function, ...$param ):void{
		var counter:int;
		looper.addEventListener( Event.ENTER_FRAME, function( $e:Event ):void{
			var i:int;
			while( i++ &amp;lt; $limit ){
				if( counter &amp;lt; $counter ){
					//$param.unshift( counter ); 필요하면 counter도 넘...
					if( $param.length ){ //rest인자는 null인 경우가 없음
						$runner.apply( null, $param );
					}else{
						$runner();
					}
					++counter;
				}else{
					looper.removeEventListener( $e.type, arguments.callee );
				}
			}
		});
	}
}
</pre>
<p>counter를 클로저로 선언했기 때문에 loop라는 함수는 동시에 몇 번이라도 불러도 잘 작동합니다.</p>
<pre class="brush: as3; title: ; notranslate">
var looper:nonBlockingLoop = new nonBlockingLoop;

looper.loop( 999999, 300, Math.random );
looper.loop( 999999, 300, getDefinition, 'flash.display.Sprite' );

function test( $val1:String, $val2:int ):void{
	trace( $val1, $val2 );
}
looper.loop( 999, 20, test, '안녕', 37 );
</pre>
<h4>결론</h4>
<p>xml파싱을 비롯하여 무거운 blocking로직은 반드시 non blocking로직으로 고쳐써야 프로그램의 다운을 막을 수 있습니다.</p>
<p>또한 non blocking loop는 화면의 갱신과 다른 이벤트의 진행을 동시에 시켜주기 때문에 유저 입장에서 훨씬 좋은 응답성을 느끼게 되고 결과적으로 더 나은 이용 경험을 하게 됩니다.</p>
<p>최근 sng등에서 더욱 하드하게 플래시를 사용하는 이상 점점 더 필수적이 되어간다 할 수 있죠.</p>
<h4>과제</h4>
<ol>
<li>non blocking loop가 완료될 때 완료이벤트를 수신할 수 있도록 개선하시오.</li>
<li>아예 Shape를 상속하도록 개선하시오.</li>
<li>일반화시켜 클래스로 정리하는 포스팅을 한 건 처음이지만 non blocking에 대해서는 이미 두어 개의 포스트가 블로그 내에 존재하니 찾아보세요.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/180/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>2011年 Coding Rule 1.0</title>
		<link>http://www.diebuster.com/flash/179</link>
		<comments>http://www.diebuster.com/flash/179#comments</comments>
		<pubDate>Wed, 12 Jan 2011 12:50:13 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[2 개발원칙]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Class project package : root로 부터 시작한다. lib package : BS11의 하위로 시작한다. ? BS11.display, BS11.media class : BS 로 시작한다. ? BStest, BSrole static을 전면 금지하고, 유일한 싱글톤 인스턴스의 이름은 언제나 SELF &#8211; BSdisplay.SELF.setData(); 추상 class : BSA 로 시작한다. ? BSAcontainer, BSAgraphics 인자 class를 사용하지 않는다. 인터페이스를 사용하지 않는다. Member [Embed : 금지 [...]]]></description>
			<content:encoded><![CDATA[<h4>Class</h4>
<ul>
<li>project package : root로 부터 시작한다.</li>
<li>lib package : BS11의 하위로 시작한다. ? BS11.display, BS11.media</li>
<li>class : BS 로 시작한다. ? BStest, BSrole</li>
<li>static을 전면 금지하고, 유일한 싱글톤 인스턴스의 이름은 언제나 SELF &#8211; BSdisplay.SELF.setData();</li>
<li>추상 class : BSA 로 시작한다. ? BSAcontainer, BSAgraphics</li>
<li>인자 class를 사용하지 않는다.</li>
<li>인터페이스를 사용하지 않는다.</li>
</ul>
<p><span id="more-179"></span></p>
<h4>Member</h4>
<ul>
<li>[Embed : 금지</li>
<li>public var : 금지</li>
<li>private var : _로 시작한다. ? private var _count:int;</li>
<li>override 되는 function : _로 시작한다 ? override private function _init():void{}</li>
<li>해당 함수가 이용하는 부가함수는 _가 끝에 붙는다. - function test() -> function test_()</li>
<li>부가함수가 여러 개일 경우 명시적으로 이름을 붙일 수 있다. - function test_module1(), function test_module2()</li>
<li>인자함수는 $로 시작한다. - function $position( $x:int, $y:int )</li>
<li>추가되는 함수는 add로 시작한다. - addChild(</li>
<li>삭제되는 함수는 Remove로 끝난다. - childRemove(</li>
<li>획득되는 함수는 Get으로 끝난다. - childGet(</li>
<li>설정되는 함수는 Set으로 끝난다. - childSet(</li>
<li>사본을 얻는 함수는 Copy로 끝난다. - childCopy(</li>
<li>초기화 함수는 Init로 끝난다. - childInit(</li>
<li>자신의 초기화 함수는 init다. - function init(</li>
</ul>
<h4>Arguments</h4>
<ul>
<li>Argument : $로 시작한다. ? function test( $a:int, $b:int );</li>
<li>인자가 Function인 경우 : $인자형인자형_반환형 으로 이름 짓는다. ? function listener( $intString_Number:Function )</li>
<li>void인 경우 반환형을 생략할 수 있다. ? function listener( $intString:Function )</li>
<li>인자가 형식있는 Array인 경우 : $형형 형태로 이름 짓는다. ? function setData( $intintString:Array )</li>
<li>인자가 형식있는 Object인 경우 : $키키 형태로 이름 짓는다. ? function setData( $idNameRegdate:Object )</li>
<li>인자가 형식있는 Param인 경우 : $키키 또는 $형형을 이용할 수 있다 - function setData( ...$intString ), function setData( ...$idName )</li>
<li>모든 인자는 기본으로 불변하고 사본을 이용한다.</li>
<li>인자로 받은 BitmapData는 반드시 dispose()시킨다.</li>
<li>이벤트 리스너 인자가 null값을 기본으로 하면 다른 곳에서도 사용된다는 의미가 된다. - function clearScreen( $e:MouseEvent = null ) <- 마우스이벤트 외에도 사용됨</li>
<li>포괄적 인자의 이름은 언제나 $param</li>
<li>식별자 계열의 인자의 이름은 언제나 $key</li>
<li>단순 값인 인자의 이름은 언제나 $val</li>
</ul>
<h4>Local Variables</h4>
<ul>
<li>결과값을 유도하는 지역변수명은 언제나 result</li>
<li>루프인자는 i, j, k, l, m ,n 까지 사용하며 그 이상의 루프인자는 허용하지 않는다 - 로직으로 분개</li>
<li>이터레이션용 인자는 언제나 key - for each( key in data )</li>
<li>key는 언제나 key:*로 선언함</li>
<li>key가 다중인 경우는 key1, key2...순으로 전개함 - for( key1 in data1 )for( key2 in data2 )</li>
<li>임시변수의 이름은 언제나 temp</li>
<li>temp가 여러 개인 경우는 실제 필요한 이름으로 대치</li>
<li>인자 덮어쓰기 또는 재활용을 금지함. - function test( $val ):void{ $val +=1; .... <- 인자를 가공하거나 재활용하는 것을 금지</li>
</ul>
<h4>Pooling</h4>
<ul>
<li>pool에서 인자를 가져올 때는 언제나 pull 메서드를 통해 가져옴 - CbitmapPool.SELF.pull();</li>
<li>pool이 auto가 아니라 수동으로 반납하는 경우는 언제나 collect로 반납함 - CbitmapPool.SELF.collect();</li>
<li>pull한 객체를 초기화시키는 과정은 호스트가 갖고 있지 않고 인자를 넘겨 pull이 처리하는 게 기본임 - CbitmapPool.SELF.pull( initData );</li>
<li>pool을 구현한 경우 해당 인스턴스는 collect메서드를 구현하고 있어야 함.? - CbitmapPool.SELF.collect( instance1 ); -> collect내부에서 instance1.collect() 를 호출하여 자원을 스스로에게 정리시킴</li>
<li>pull우선 pooling을 기본으로 사용함</li>
</ul>
<h4>Basic</h4>
<ul>
<li>function get, set을 기본적으로 금지한다.</li>
<li>문자열은 기본적으로 작은 따옴표로 묶는다.</li>
<li>클래스는 기본적으로 final이다.</li>
<li>클래스는 기본적으로 internal 이다.</li>
<li>메서드는 기본적으로 private, internal, public 순으로 권한을 풀어준다.</li>
<li>메서드가 private가 아닌 경우 반드시 final을 기본으로 선언한다.</li>
<li>this. 등의 강제적인 컨텍스트 할당을 금지한다.</li>
<li>외부라이브러리에 대해 래핑 없이 쓰는 것을 금지한다. - tweenMax.to( <- 금지 -> BStween.to(</li>
</ul>
<h4>Expression</h4>
<ul>
<li>내용있는 괄호는 반드시 공백을 둔다. ? ( 3 )</li>
<li>내용없는 괄호는 공백을 두지 않는다. ? test()</li>
<li>*와 /는 공백을 두지 않는다 ? 3*4,? 5/2</li>
<li>그 외의 모든 연산자는 공백을 둔다. ? 3 + 2, 5 + 2</li>
<li>괄호 및 위의 모든 규칙을 혼용한다. ? 3 + 2*3 + ( 1+ 3 )*5 > 5</li>
<li>컴마는 뒤에 오는 요소가 공백을 둔다. ? 3, 5, 7</li>
<li>대괄호는 공백을 두지 않는다. ? [3, 4]</li>
<li>시작 중괄호는 옆에 붙인다. ? if( test > 3 ){</li>
<li>중괄호 생략을 금지한다. ? if( test > 3 ) a = 3; 금지</li>
<li>?:의 삼항연산자를 금지한다. ? return a ? a : 3; 금지</li>
<li><a href="mailto:‘@’">‘@’</a>는 플랫폼에 예약되는 특수한 문자임</li>
<li>부정식을 금지함 &#8211; if( !a ) -> if( a ){}else{</li>
<li>성능부분에는 bool식을 음수평가로 대체한다. &#8211; if( a ) -> if( a<0 )<!--EndFragment--></li>
<li>성능 상의 이유로 &#038;&#038;를 다중 if로 사용하는 경우 이어쓰기를 허용한다. ? if( test )if( check )if( isObject ){</li>
<li>루프는 언제나 (? j = container.length ; i < j ; ++i ) 를 기반으로 사용한다.</li>
<li>비교연산자는 임의평가식, 항등비교, 일반비교 순으로 사용한다.</li>
<li>형비교는 toString, is, in, typeof 순으로 사용한다.</li>
</ul>
<p>*TAF는 별도 규격들이 있음</p>
]]></content:encoded>
			<wfw:commentRss>http://www.diebuster.com/flash/179/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

