==> 젤리빈에서는 .... RenderScript를 버렸다네요.

그래서 SurfaceView의 대안으로 TextureView를 사용하도록 권고하네요. ^^


렌더스크립트는



아키텍처 종속성에 대한 문제(안드로이드는 ARM, MIPS, X86 과 같은 다양한 플랫폼에서 동작한다.) 및 성능과 사용성 모두를 유연하게 해결하기 위한 구글의 솔루션이다.


렌더스크립트는 기기 아키텍처에 종속적이지 않으며, 네이티브 코드로 작성한것과 같이 성능은 좋다.


또한 최근 개발자의 관심사인 병렬CPU사용도 용이하다.


하지만 렌더스크립트는 C/C++로 작성된 코드와 달리 안드로이드 환경에서만 이용할 수 있다는 단점도 있다.


다시 정리해 보면,


   렌더스크립트는 고성능 3D렌더링 컴퓨팅 연산에 맞춘 새 API 이다.
   렌더스크립트의 목적은 저수준에서 얻은 고성능의 API앱성능을 최적화 하기위해 기계수준까지 작업
     하는것이
익숙하지 않은 안드로이드 개발자에게 주는것이다.
   렌더스크립트는 하드웨어 가속되는 간단한 3D 렌더링 API, CUDA와 유사한 개발자 친화적인 연산 API,
     C99
와 유사한 언어이다.
   간단한 스크립트는 작업을 GPU에서 연산하고, 더 복잡한 스크립트는 CPU에서 수행한다.


렌더스크립트의 동작방식


렌더스크립트를 사용할때 스크립트 자체는 호스트머신에서 LLVM(Low-Level  Virtual Machine)의 비트코드로 컴파일되며 LLVM 비트코드는 안드로이드 디바이스에서 네이티브 코드로 컴파일 된다.


디바이스에 있는 네이티브 코드는 캐시되고, 그다음에 실행될 스크립트는 스크립트 자체가 이미 네이티브로 컴파일 되어 있기 때문에 실행속도가 빠르다.


비트코드를 네이티브 코드로 컴파일 하는 작업은 디바이스에서 일어나기 때문에 네이티브 코드는 디바이스에 최적화되거나 GPU와 같은 하드웨어 모듈에 최적화 된다.


그러므로 개발자는 아키텍처와 각각의 기능을 어떻게 고려해야 할지 고민할 필요가 없다. 이런면에서 보면 NDK와 가장 큰 차이점이라고 볼 수 있다.


즉 어떤 디바이스를 사용하느냐에 따라 개발하는 애플리케이션에서 어떤 코드를 사용할지 결정하게 되므로 SIMD 명령어를 사용하려고 한다면 NEON을 사용할 수 있는지 미리 체크해야 한다. 


렌더스크립트는 네이티브 레벨로 동작하지만 작동은 안드로이드 VM이 관할한다. 


다음은 렌더스크립트의 시스템 구조이다.




<참고> 

SIMD?

 Single Instruction Multiple Data. 병렬 프로세스의 한 종류로, 하나의 명령어로 여러개의 값을 동시에 계산하는 방식이다. 벡터 프로세스에서 많이 사용되는 방식으로 비디오 게임콘솔이나 그래픽카드와 같은 멀티미디어 분야에 자주 사용된다.

 

NEON이란?

  ARM 프로세서에서 사용하는 Advanced SIMD 확장 도구를 NEON이라 부르며 미디어와 신호처리를 위한 명령어집합을 추가지원한다.래서 오디오, 비디오, 3D, 이미지, 음성 인식등의 처리를 프로세서 수준에서 빠ㅡ게 수행해준다. 

 

VFP?

  Vector Floating Point는 ARM에서 IEEE부동소수점 연산 표준을 만족하는 단정도(single-precision)과 배정도(double-precision) 부동 소수점 연산을 제공하는 보조 연산장치이다.  Vector라는 단어에도 불구하고 진정한 SIMD 를 제공한다고 보기는 힘들지만 ARM에서 실수 연산의 성능 향상에 기여하고 있다.



자 그럼 프로젝트에서 기본적인 렌더스크립트를 어떻게 생성하는지 같이 해 보자.

에뮬레이터는 렌더스크립트에서 요구하는 모든 기능을 제공하고 있지 않기 때문에 렌더스크립트를 테스트하려면 실제 안드로이드 디바이스가 필요하다.


언제나 시작은 "안녕 세상아 !!"  ㅎㅎ

렌더스크립트를 사용해서 출력해보도록 하자.


STEP1. 안드로이드 프로젝트를 생성한다. (허니콤도 상관없지만 ICS로 타켓환경을 정하자.)

STEP2. src폴더에 helloworld.rs 파일을 하나 생성한다.

   #pragma version(1);
   #pragma rs java_package_name(kr.co.openeg.render)
   void hello_world() {
         reDebug("hello world!!",0);
   }


1번라인은 렌더스크립트의 버전

2번라인은 이스크립트가 사용할 자바의 패키지명 선언

4번라인은 로그캣엔 출력할 문자열


안드로이드 프로젝트를 빌드하면 다음의 세개 파일이 생성된다.

- gen/ScriptC_helloworld.java

- gen/helloworld.d

- res/raw/helloworld.bc



먼저 gen폴더에 생성된 ScriptC_helloworld.java  파일을 보면,  #pragma 로 정의한 패키지이름이 정의되어 있고, 디폴트 생성자가 만들어져 있으며, invoke_hello_world함수가 생성된것을 볼 수 있다.

/*
 * Copyright (C) 2011-2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This file is auto-generated. DO NOT MODIFY!
 * The source Renderscript file: C:\Users\young0\workspace_android\renderscript\src\kr\co\openeg\render\helloworld.rs
 */
package kr.co.openeg.render;

import android.renderscript.*;
import android.content.res.Resources;

/**
 * @hide
 */
public class ScriptC_helloworld extends ScriptC {
    private static final String __rs_resource_name = "helloworld";
    // Constructor
    public  ScriptC_helloworld(RenderScript rs) {
        this(rs,
             rs.getApplicationContext().getResources(),
             rs.getApplicationContext().getResources().getIdentifier(
                 __rs_resource_name, "raw",
                 rs.getApplicationContext().getPackageName()));
    }

    public  ScriptC_helloworld(RenderScript rs, Resources resources, int id) {
        super(rs, resources, id);
    }

    private final static int mExportFuncIdx_hello_world = 0;
    public void invoke_hello_world() {
        invoke(mExportFuncIdx_hello_world);
    }

}



STEP3. 생성한 스크립트를 사용하는 액티비티를 생성해 보자.

자바코드에서 ScriptC_helloworld 인스턴스를 생성하여 invoke_hello_world메서드를 통해 스크립트의 hello_world함수를 호출한다.



package kr.co.openeg.render;

import android.os.Bundle;
import android.renderscript.RenderScript;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		RenderScript rs=RenderScript.create(this);
		ScriptC_helloworld hws=new ScriptC_helloworld(rs, getResources(), R.raw.helloworld);
		hws.invoke_hello_world();
	}
}


STEP4. 실행결과를 로그캣에서 확인한다.

이 앱을 실행하면  로그캣에 다음과 같은 메시지가 출력되는것을 확인할 수 있다.

02-20 14:50:34.444: D/RenderScript(592): hello world!! 0  0x0




이번에는 실제 렌더스크립트를 이용하여 렌더링 작업을 수행해 보자



STEP1. 렌더링을 하는 스크립트를 생성한다.

STEP2. RenderScriptGL 컨텍스트 객체 생성

STEP3. RSSurfaceView를 확장한 클래스 생성

STEP4. 액티비티에서 컨텐트 뷰 역할을 하기위한 RSSurfaceView 설정



STEP1. 렌더링 스크립트의 생성

여기서는 백그라운드 컬러를 변경하는 스크립트를 작성해보자.

regClearColor()함수를 사용하기위해 rs_graphics.rsh를 include한다..

init()함수는 스크립트가 생성될때 자동으로 호출되며, 다른 루틴이 실행되기전에 초기화 작업이 필요하다면 이 함수에 정의한다.

root()함수는 실제 렌더링을 하는 함수이다. 이 예제에서는 랜덤컬러로 백그라운드 색상을 변경하는 간단한 코드이다. return되는 값은 얼마나 자주 렌더링이 발생할지를 설정하는 값이다. 이예제에서는 20밀리초마다 프레임이 업데이트 된다.


#pragma version(1)
#pragma rs java_package_name(kr.co.openeg.render)
#include "rs_graphics.rsh"

void init() {
   // 초기화에 필요한 작업을 수행한다.
}

int root() {
    float red=rsRand(1.0f);
    float green=rsRand(1.0f);
    float blue=rsRand(1.0f);
    rsgClearColor(red,green,blue,1.0f);
    return 20;
}


STEP2. RenderScriptGL 컨텍스트 객체 생성

렌더링작업을 수행하는 기능을 가진 HelloRenderingRS 클래스를 생성한다.  예제 코드에서는 실제 ScriptC_changeBackgroundColor 스크립트를 생성하여 RenderScriptGL 객체와 연결하는 작업을 수행한다. bindRootScript()함ㅁ수를 호출하면 스크립트의 root()함수가 호출되어 렌더링 작업을 수행하게 된다.


package kr.co.openeg.render;

import android.content.res.Resources;
import android.renderscript.RenderScriptGL;

public class HelloRenderingRS {
	
	private Resources mResource;
	private RenderScriptGL mRS;
	private ScriptC_changeBackgroundColor mScript;
	
     public HelloRenderingRS() {}
     
     public void init(RenderScriptGL rs, Resources res){
    	 mRS=rs;
    	 mResource = res;
    	 mScript = new ScriptC_changeBackgroundColor(mRS, mResource, R.raw.changebackgroundcolor);
    	 mRS.bindRootScript(mScript);
     }
     
}

STEP3. RSSurfaceView를 확장한 클래스 생성

RenderScriptGL 컨텍스트 객체를 생성하기 위한 RSSurfaceView를 상속받은 HelloRenderView클래스를 생성한다.


package kr.co.openeg.render;

import android.content.Context;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.view.SurfaceHolder;

public class HelloRenderView extends RSSurfaceView {
      public HelloRenderView(Context context) {
    	  super(context);
      }
      
      private RenderScriptGL mRS;
      private HelloRenderingRS mRender;
      public void surfaceChanged(SurfaceHolder holder, int format, int w, int h){
    	  super.surfaceChanged(holder, format, w, h);
    	  if(mRS == null) {
    		  RenderScriptGL.SurfaceConfig sc=new RenderScriptGL.SurfaceConfig();
    		  mRS=createRenderScriptGL(sc);
    		  mRender=new HelloRenderingRS();
    		  mRender.init(mRS, getResources());    		  
    	  }
    	  mRS.setSurface(holder, w, h);
      }
	@Override
	protected void onDetachedFromWindow() {
		// TODO Auto-generated method stub
		if(mRS != null) {
			mRS=null;
			destroyRenderScriptGL();
		}
	}
      
      
}


STEP4. 액티비티에서 컨텐트 뷰 역할을 하기위한 RSSurfaceView 설정

마지막으로 렌더링 작업을 수행하는 액티비티를 작성한다. 이 예제에서는 화면이 포커스를 얻게 되는 시점에 렌더링 작업을 시작하고 포커스를 잃게 되는 시점에 렌더링 작업을 잠시 멈춘다.


package kr.co.openeg.render;

import android.os.Bundle;
import android.renderscript.RenderScript;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

	HelloRenderView helloView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
			
		RenderScript rs=RenderScript.create(this);
		ScriptC_helloworld hws=new ScriptC_helloworld(rs, getResources(), R.raw.helloworld);
		hws.invoke_hello_world();
		
		helloView = new HelloRenderView(this);
		setContentView(helloView);
	}
	
	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
}


STEP5. 실행결과 확인

실제 실행한 결과를 보면 20밀리초마다 화면의 백그라운드 색상이 빨강,초록,파랑색중 랜덤하게 업데이트 된다.



안드로이드 4.0에서는 두가지 중요한 기능이 추가되었다.

- 애플리케이션은 오프스크린 버퍼로 메모리를 사용하고 싶을 때 Allocation.USAGE_GRAPHICS_RENDER_TARGET 플래그를 설정하여 오프스크린 버퍼로 메모리를 사용할 수 있다.

- 레이아웃에서 RSTextureView를 사용할 수 있다. RSSurfaceView를 사용하면 분리된 윈도우를 생성하지만 RSTextureView를 사용하면 그렇지 않다.





렌더스크립트에 대한 또다른 예제는 다음 링크에서 더 테스트해 볼수 있을것 같다.

http://blog.naver.com/PostView.nhn?blogId=719121812&logNo=20171662334



네이티브 렌더스크립트 API


렌더스크립트는 API사용이 제한되어 있어서 NDK나 C라이브러리의 API를 호출할 수 없다. 렌더스크립트의 API는 6개의 헤더파일에 정의 되어있는데 SDK의 platform 폴더에 각 안드로이드 버전별로 rendscript 폴더에 저장되어 있다.


허니콤에서는 6개의 헤더파일이 제공된다.

rs_type.rsh               

rs_core.rsh              

rs_cl.rsh                  

rs_math.rsh

rs_graphics.rsh

rs_time.rsh 


여기에 ICS는 아래  6개의  헤더파일이 더 추가되어 12개의 헤더파일을 제공한다.

rs_allocation.rsh

rs_atomic.rsh

rs_debug.rsh

rs_matrix.rsh

rs_object.rsh

rs_quaternion.rsh


블로그 이미지

오픈이지 제로킴

시큐어코딩 교육/컨설팅 전문가 그룹

티스토리 툴바