2011년 11월 10일 목요일

렌더스크립트 2부

[이 글은 그래픽, 성능 튜닝, 소프트웨어 아키텍쳐 전문인 안드로이드 엔지니어 R. Jason Sams가 작성한 것입니다. - Tim Bray] (Renderscript Part 2의 번역입니다.)


렌더스크립트(Renderscript)를 소개합니다에서 기술의 짧은 개요를 보였습니다. 이 글에서는 "계산"에 대해 깊게 다룹니다. 렌더스크립트에서 "계산"은 달빅 코드에서 렌더스크립트 코드로 데이터 처리를 떠넘기는 것을 의미합니다. 렌더스크립트는 동일한 프로세스에서 수행될 수도 있고 다른 머신에서 수행될 수도 있죠.

렌더스크립트의 설계 목표

렌더스크립트는 3개의 기본 목표가 있습니다. 여기에 중요한 것부터 덜 중요한 순으로 나열하였습니다.

이식성: 애플리케이션의 코드는 모든 장비에서 수행되어야 합니다. 심지어 근본적으로 다른 장비에서도 수행되어야합니다. 현재 ARM은 다양한 변형을 가집니다. VFP가 없을 수도 있고요. NEON이 없을 수도 있죠. 레지스터 개수도 다양합니다. (주: 허니컴이 이식된 테그라 2가 NEON이 없고 VFP를 위한 레지스터 수가 부족합니다.) ARM을 넘어서면 x86을 비롯해서 다양한 CPU  아키텍쳐가 있습니다. GPU도 다양하고 DSP는 더 다양합니다.

성능: 두번째 목표는 이식성의 제한 아래에서 최대 성능을 얻는 것입니다. 렌더스크립트에선 기존 해법 보다 더 나은 성능을 이루는 게 우리 뜻입니다.

편의성: 세번째 목표는 가능한 개발을 단순하게 하자는 것입니다. 글루 코드 (주: 다른 환경의 코드를 결합시키기 위한 코드)와 기타 개발자들을 바쁘게 하는 단계를 자동화하는 겁니다.

이 세가지 목표에 의해 다양한 설계 취사 선택(trade-offs)을 가집니다. 이 설계 취사 선택이 장비에서 렌더스크립트를 달빅과 NDK와 같은 기존의 접근 방법과 차이를 만들었습니다. 다른 문제를 풀기 위해 다른 도구를 쓰는 것처럼요.

핵심 설계 결정

첫째로 우리가 어떤 언어를 쓸지 결정했습니다. 언어를 선택하려니 거의 무한한 선택지가 있었습니다. 쉐이딩 스타일 언어, C, C++가 고려되었죠. 씬 그래프 같은 그래픽 응용에 필요한 자료 구조를 다룰 필요가 있어서 쉐이딩 언어는 뺐습니다. 포인터와 재귀 구문을 빼면 너무 불편하고요. C++은 매력적이지만 이식성의 문제가 있었습니다. C++의 고급 기능들은 CPU가 없는 하드웨어에서 수행되기 곤란합니다. 결국 우리는 렌더스크립트가 C99를 기반으로 하도록 하였습니다. 우리가 원하는 만큼 성능을 제공하고 개발자에게 친숙하며 많은 하드웨어에서 문제가 없기 때문이죠.

다음 설계 취사 선택은 작업 흐름입니다. 우리는 특히 소스 코드를 어떻게 기계 코드로 바꾸느냐에 중점을 두었습니다. 렌더스크립트 개발 중에 우리는 여러 선택지를 탐구했고 두 개의 다른 방법을 구현해보았습니다. (이클레어 부터 진저브레드까지의) 옛날 판은 C 소스 코드를 장비의 기계 코드로 바로 컴파일하였습니다. 이 방법은 애플리케이션을 위해 즉시 소스를 만드는 것 까지는 동일했지만 편의성 문제가 있었습니다. 앱을 컴파일하고 설치하고 수행하면 고통스러운 문맥 에러가 보였습니다. 또 성능이 약한 CPU를 가진 장비에서는 정적 분석과 수행할 수 있는 최적화의 범위가 제한되는 문제가 있었습니다. (주: 이전 렌더스크립트는 acc 컴파일러 기반이지만 허니컴부터 LLVM 기반으로 변경되었습니다.)

수정된 clang을 이용하는 LLVM(주: clang은 LLVM 프로젝트의 C 언어 구현입니다.)으로 교체한 결과 호스트에서 스크립트(주: 렌더스크립트를 모두 스크립트로 줄여부릅니다.)를 컴파일하고 분석하는 모델로 갈 수 있었습니다. 이 단계에서 높은 수준의 최적화를 수행하여 LLVM 비트코드 (bitcode) 를 만들었습니다. 중간단계의 비트코드에서 기계 코드로 바꾸는 처리는 여전히 장비에서 이루어집니다. (추가적인 장비 특화의 최적화와 함께요.)

마지막 큰 계산을 위한 취사 선택은 쓰레드 시작입니다. 이 취사 선택은 성능과 이식성 사이에 있습니다. 주어진 정보로 기존 계산 방식를 사용하면 특정 하드웨어에만 최적화되고 다른 하드웨어에선 손해입니다. 시간이 무한하고 개발자가 충분하면 모든 하드웨어 조합마다 최적화할 수 있습니다. 다양한 장비에서 실험하고 최적화하는 것은 결코 나쁘지 않지만 그들이 가지지 않은 미 출시된 하드웨어를 위한 최적화는 불가능합니다. 더 이식성 있는 방법은 최고 성능에 드는 비용을 이용하여 평균 이상의 성능을 제공할 수 있도록 실시간에 부담을 주는 겁니다. 이식성을 첫째 목표로 두었기 때문에 우리는 실시간에 부담을 주기로 결정했습니다.

실시간 쓰레드 개시 관리를 선택하여 얻은 두번째 효과는 스크립트가 어디에서 수행될지가 동적으로 결정되는 점입니다. 예를 들어 어떤 계산 하드웨어는 포인터와 재귀를 지원하지만 어떤 것은 못합니다. 우리는 이런 걸 막고 낮은 공통 분모의 API를 개발자에게 제공할 수도 있었습니다. 하지만 우리는 그 대신 실시간 스크립트 분석을 선택했습니다. 개발자는 이 기능들을 제공하는 하드웨어를 최대한 활용할 수 있습니다. 언제나 충분한 기능을 지원하는 CPU에게 의지할 수 있기 때문입니다. 결국 개발자는 좋은 앱을 작성하는 것에 집중할 수 있고 하드웨어 제조사는 가장 기능이 충실하고 효과적인 하드웨어를 만드는 것을 완성할 수 있습니다. 새 기능이 나오면 애플리케이션은 코드 수정없이 그 이점을 얻게 됩니다.

편의성은 렌더스크립트의 주요 동인(driver)였습니다. 기존의 대부분 계산과 그래픽 플랫폼은 정성들인 글루 로직 (주: 이종 환경의 코드들을 서로 이어주는 부분) 으로 높은 성능의 코드를 핵심 애플리케이션 코드와 결합하였습니다. 이 코드는 결함을 만들 가능성이 높고 대체로 작성하기 어렵습니다. 호스트 렌더스크립트 컴파일러에서 수행하는 정적 분석이 이 문제를 푸는데 도움이 됩니다. 각 사용자 스크립트는 달빅의 "글루" 클래스를 생성합니다. 글루 클래스의 이름과 접근자는 스크립트의 내용에서 도출됩니다. 이게 달빅으로부터 스크립트 사용을 매우 간결하게 합니다.

예: 애플리케이션 수준

주어진 취사 선택을 사용하면 간단한 계산 애플리케이션은 어떤 모습일까요? 이 매우 기초적인 예제에선 일반적인 android.graphics.Bitmap을 취하고 이것을 단색으로 변경하여 두번째 bitmap으로 복사하는 스크립트를 수행합니다. 스크립트 자체를 보기 앞서 스크립트를 수행하는 애플리케이션 코드를 봅시다. 이는 SDK 샘플의 HelloCompute에서 가져온 것입니다.

    private Bitmap mBitmapIn;
    private Bitmap mBitmapOut;
    private RenderScript mRS;
    private Allocation mInAllocation;
    private Allocation mOutAllocation;
    private ScriptC_mono mScript;

    private void createScript() {
        mRS = RenderScript.create(this);

        mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
                                                    Allocation.MipmapControl.MIPMAP_NONE,
                                                    Allocation.USAGE_SCRIPT);
        mOutAllocation = Allocation.createTyped(mRS, mInAllocation.getType());

        mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);

        mScript.set_gIn(mInAllocation);
        mScript.set_gOut(mOutAllocation);
        mScript.set_gScript(mScript);
        mScript.invoke_filter();
        mOutAllocation.copyTo(mBitmapOut);
    }
이 함수는 두개의 비트맵이 이미 생성되어 있고 같은 크기와 형태라고 가정합니다.

렌더스크립트 애플리케이션이 필요한 첫번째는 context 객체입니다. 이것은 다른 렌더스크립트 객체 전부를 생성하고 관리하는 핵심 객체입니다. 함수의 첫줄은 그 객체 mRS를 생성합니다. 이 객체는 애플리케이션이 이 객체를 사용하거나 이 객체로 만든 다른 객체를 사용하는 동안 살아있어야 합니다.

다음 두 함수 호출은 Bitmap을 위한 계산 할당 (allocation) 입니다. 렌더스크립트는 고유의 메모리 할당자 (allocator) 를 가집니다. 메모리가 잠재적으로 여러 프로세스에 공유될 수 있고 하나 이상의 메모리 주소에 존재할 수 있기 때문입니다. 할당자가 생성될 때 잠재적인 용법들이 나열되어 시스템이 현재 사용에 맞는 메모리 타입을 선택하게 됩니다.

첫번째 함수 createFromBitmap()는 적합한 렌더스크립트 할당 객체를 만들고 할당 안에 비트맵의 내용을 복사합니다. 할당은 렌더스크립트의 메모리 사용의 기본 단위입니다. createTyped()은 구조적으로 첫번째 할당과 동일한 두번째 할당을 만듭니다 구조의 정의는 첫째 할당을 getType()을 질의해 얻습니다. 렌더스크립트 형은 할당의 구조를 정의합니다. 이 경우 형은 높이, 너비, 입력된 비트맵의 형태에서 만들어집니다.

다음 줄은 "mono.rs" 이름의 스크립트를 읽습니다. R.raw.mono가 이를 확인합니다. (주:  R.raw.명칭에 렌더스크립트 파일명이 들어 있습니다.) 스크립트는 애플리케이션의 APK안의 raw 리소스로 포함됩니다. 자동으로 생성된 "글루" 클래스의 이름 ScriptC_mono를 유의하세요.

다음 세줄은 "글루" 클래스에 생성된 메서드를 이용해 스크립트의 속성들(properties)을 설정합니다.

이제 우리는 모든 것을 설정했고요. invoke_filter() 함수가 실제로 우리를 위해 어떤 일을 수행합니다. 스크립트의 filter() 함수가 호출되고 만약 이 함수가 인자를 받는다면 여기에서 전달됩니다. 반환값은 호출이 비동기이기 때문에 허용되지 않습니다.

함수의 마지막 줄은 계산 스크립트의 결과를 다시 관리되는 (managed) 비트맵에게 복사합니다. 이 코드는 동기화 코드가 내장되어 있어 스크립트가 완전히 수행된 것을 확인합니다. (주: "관리되는 (managed)"은 마이크로소프트가 닷넷을 소개하며 표현한 문구로 가상 머신에서 수행되는 것을 의미합니다. 렌더스크립트 계산 결과를 자바 객체로 복사했다는 의미입니다.)

예: 스크립트

mono.rs에 저장된 이 렌더스크립트는 위의 렌더스크립트 코드에서 호출됩니다.

#pragma version(1)
#pragma rs java_package_name(com.android.example.hellocompute)

rs_allocation gIn;
rs_allocation gOut;
rs_script gScript;
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
void root(const uchar4 *v_in, uchar4 *v_out, const void *usrData, uint32_t x, uint32_t y) {
    float4 f4 = rsUnpackColor8888(*v_in);

    float3 mono = dot(f4.rgb, gMonoMult);
    *v_out = rsPackColorTo8888(mono);
}
void filter() {
    rsForEach(gScript, gIn, gOut, 0);
}

첫번째 줄은 단순히 컴파일러에게 이 스크립트를 작성한 네이티브 렌더스크립트 API  리비전이 무엇인지 알려주는 것입니다. 두번째 줄은 생성된 반영 코드 (reflected code) 와 연관된 패키지를 조절합니다. (주: 반영 코드는 다른 환경의 내용을 보고, 변경할 수 있는 코드를 의미합니다. 여기에선 렌더스크립트의 속성을 자바에서 변경할 수 있게 해주는 글루 코드를 의미합니다.)

나열된 세 전역 변수는 우리의 관리되는 코드(managed code)에 설정한 전역변수와 관련이 있습니다. 네번째 전역 변수는 static이기 때문에 반영되지 않습니다. 비 정적 (non-static), 상수(const), 전역에 대해서만 반영된 메서드를 생성합니다. 스크립트와 관리되는 (managed) 코드 사이의 동기화에서 상수가 유지되는 것은 꽤 유용합니다.

함수 root()는 렌더스크립트에서 특별합니다. 개념적으로 C의 main()과 비슷하죠. 실시간으로 스크립트가 수행될 때 이 함수가 호출됩니다. 이 경우 매개 변수는 우리 할당의 입력, 출력 픽셀입니다. 요청이 처리할 할당의 주소를 보통의 포인터로 사용할 수 있습니다. 이 예제에서는 해당 매개 변수를 사용하지 않습니다.

저 root 함수의 세번째 줄은 RGBA_8888으로 부터 4개의 float형의 벡터로 픽셀을 푸는 것입니다. (주: 렌더스크립트는 float 자료형이 뭉쳐 벡터형을 만들고 이를 float3, float4라 부릅니다. vec3, vec4라 부르는 OpenGL과는 조금 다른 컨벤션을 가지고 있습니다.) 그 다음 줄은 입력된 픽셀 데이터과 흑백 상수들(monochrome constants)를 내장 수학 함수를 사용해 내적(dot product)하여 회색 수준(grey scale)을 계산합니다. 내적이 하나의 float을 리턴할 수 있지만 값이 단순히 float3의 x,y,z 요소로 복제된 float3을 반환한다는 점을 유의하세요. (주: 3D 그래픽에서 벡터의 요소를 x, y, z, w라고 부릅니다. 이는 좌표를 의미하기도 하며 컬러 rgba를 순서대로 의미하기도 합니다.)  마지막으로 우리는 또 내장 함수를 사용하여 float형들을 32비트 픽셀로 다시 묶습니다. 여기서 쓰여진 것은 오버로드된 함수의 예인데요. rsPackColorTo8888가 RGBA (float4)를 받지 않고 RGB (float3)를 받는 것을 볼 수 있습니다. A를 쓰지 않으면 오버로드 함수에 의해 이 값이 1.0f로 여겨지죠.

filter() 함수는 관리된 코드에서 변환을 위해 부릅니다. 이걸 단순히 할당의 매 요소마다 호출하죠. 첫 번째 매개 변수는 수행될 스크립트입니다. 이 스크립트의 root 함수가 할당의 매 요소를 위해 호출됩니다. 두 번째 세 번째 매개 변수는 입력과 출력 자료 할당들입니다. 마지막 매개 변수는 우리가 추가적은 정보를 root 함수에 전달하기 원할 때 사용하는 사용자 데이터용 포인터입니다.

forEach는 장비가 다중 프로세서를 지원한다면 여러 쓰레드를 사용합니다. 향후 forEach는 하나의 프로세서에서 다른 프로세서로 제어를 넘기는 전환점을 제공하게 됩니다. 이 예제의 filter()가 CPU에서 수행되고 root()가 GPU나 DSP가 수행되는 것을 기대할 수 있을 겁니다.

저는 이 글이 렌더스크립트 뒤의 설계에 대한 일별과 예제 코드가 동작하는 방법을 전달했으면 합니다.

아시아에서 열리는 안드로이드 개발자 랩들

[이 글은 Tim Bray에 의해 작성된 More Android Developer Labs in Asia의 번역입니다.]

몇달 전 우리는 아시아, 북미, 유럽에서 안드로이드 랩 (Android Develop Labs) 시리즈를 시작했습니다. 2011년 시리즈를 마무리 짓기 위해 우리는 아시아 두 지역에서도 등록을 열었습니다.

  • 태국 - 2011년 12월 2일
  • 홍콩 - 2011년 12월 6일

ADL (안드로이드 개발자 랩) 시리즈가 또다른 안드로이드 입문 세션이나 일반적인 개론이 아니란 것을 상기시켜드립니다. 이것은 타블렛 용 안드로이드 앱을 최적화하는 걸 목표로 합니다. 특히 윤택이 나고 사용자 경험이 강조된 높은 품질의 타블렛 앱을 만드는 것요.

등록은 두 단계입니다. 아무나 등록은 할 수 있지만 우리가 수용할 수 있는 참여자는 등록자에 비해 상대적으로 적습니다. 그들이 만든 안드로이드 앱을 기준으로 해서 품질에서 높은 수준에 달할 수 있는 잠재력이 있고, 거기에 맞고, 이미 달성한 사람들을 수용합니다. 우리의 목표는 당신의 앱을 ADL에 가져와 안드로이드 타블렛 사용자가 미소지을 수 있게 향상하는 것입니다.

2011년 11월 2일 수요일

Dalvik JIT

2010년 5월 25일 2:57PM - Tim Bray 작성
원문은 Dalvik JIT입니다.
[이 게시물은 가상 머신의 카우보이, Dan Bornstein이 작성하였습니다. — Tim Bray]

안드로이드 프로젝트의 달빅 팀의 기술 리더로서, 저는 가상머신(VM)과 안드로이드 어플리케이션 프레임워크 밑에 자리하는 코어 클래스 라이브러리에 해당하는 작업을 처리하는 데 시간을 보냈습니다. 이 계층은 최종 사용자에게는 거의 드러나지 않습니다. 하지만 이것이 견고해지면 안드로이드 디바이스가 부드럽게 동작하고 개발자의 생산성을 향상시키는 데 일조할 것입니다.
릴리즈 2.2 버전은 특히 제게 만족스러운 버전입니다. 1.0 이전 버전 이래로 완전히 새로운 VM 기술을 제공할 수 있었기 때문이죠. 우리 팀과 제가 지금까지 해왔던 일과는 다르게 최종 사용자도 직접적으로 경험할 수 있는 그런 것이었습니다.
“Dalvik”은 분명 잘 알려진 단어가 아닙니다(적어도 저희 나라에서는요). 그리고, 대부분은 눈 앞에 있는 것이 가상머신인지도 모를 것입니다. 하지만, 여러분이 현재 디바이스를 더 좋게 — 더 빠르게 동작하고, 배터리는 덜 사용하게 — 되었다고 말한다면 사실상 눈길을 끈 것이죠!

어떻게 실현해 내었을까요?

우리는 Just In Time (JIT) 컴파일러를 달빅 가상머신에 추가했습니다. JIT는 소프트웨어 요소로서 어플리케이션이 여전히 실행하고 있는 동안에 어플리케이션 코드를 받아서, 분석하고, 더 빠르게 동작하는 형태로 번역합니다. 달빅 JIT의 디자인에 대해 더 자세히 알고 싶다면 Google I/O 2010에 제 동료인 Bill Buzbee와 Ben Chenge이 멋진 발표를 보시기 바랍니다. 이들은 곧 YouTube에 올라갈 것입니다.
정확히 말하자면, 차이점이 항상 극적인 것은 아닙니다. 또한 모든 어플리케이션에 균일하게 적용되는 것도 아닙니다. CPU를 최대한 사용하도록 작성된 코드는 이제 동일한 시간 동안에 더 많은 일을 할 수 있고(즉, 더 빨리 수행되고), 제한된 기준동안 작업을 수행하도록 작성된 코드는 이제 더 적은 시간과 더 적은 CPU를 사용해 일을 끝낼 수 있게됩니다(즉, 더 적은 배터리를 사용해서). 특히, 우리는 CPU 활용도가 최상일 때 성능 부분에서 실제로 이전 달빅 VM 버전에 비해 2배에서 5배의 향상을 보았습니다. 이는 더욱 전통적인 인터프리터 구현보다 4배에서 10배 정도 빠른 것과 동일합니다.
우리 팀은 새로운 JIT을 전반적으로 자랑스럽게 생각합니다. 하지만 특히 두 가지 측면을 강조하고 싶습니다:
이전의 많은 JIT 구현은 느리게 반응했습니다. 늦은 준비 운동 이후에만 성능 향상을 제공했죠. 극적인 경우에는, 코드가 완전히 빨라질 때 까지 몇 분이 될 수도, 몇 시간이 될 수도 있습니다. 반면 달빅 JIT는 곧바로 반응합니다. 여러분이 즐겨하는 게임의 "시작" 버튼을 누르고 겨우 얼마 지나지 않아서 이미 JIT로부터 혜택을 받게 됩니다.
우리는 JIT이 아주 적게 사용하는 메모리량에 대해서도 기쁘게 말씀드리고 싶습니다. JIT 코드 자체는 100k도 되지 않습니다. 그리고 JIT 위에서 수행되는 각 프로세스는 각자 램이나 그 외 공간의 또 다른 100k를 더 사용할 뿐입니다. 현재 세대의 안드로이드 폰에서 사용자는 추가적인 메모리 사용률은 느끼지도 못 할 정도입니다. 제 폰의 경우 여전히 말 그대로 열 두개의 어플리케이션이 메모리 위에 올라가 바로 실행될 준비가 되어 있습니다.
달빅 팀은 이런 성과에 만족하고 쉬지 않습니다. 우리는 달빅 JIT가 앞으로 몇 달 동안 많은 디바이스에 사용되길 바랍니다. 우리 팀은 VM과 라이브러리 코드를 더 좋게 만들 아이디어 목록들이 끝없이 있으며, 이들에 대해 부지런이 작업하고 있습니다.

2011년 10월 27일 목요일

렌더스크립트(Renderscript)를 소개합니다.


이 글은 그래픽, 성능 튜닝, 소프트웨어 아키텍쳐에 정통한 안드로이드 엔지니어 R. Jason Sams가 작성하였습니다. - Tim Bray (Introducing Renderscript의 번역입니다.)

렌더스크립트는 이전에 자세히 다룬적 없는 허니컴의 새 핵심 기능입니다. 여기선 이를 두 부분으로 나누어 설명할텐데요. 이 글은 렌더 스크립트에 대한 짧은 개론입니다. 간단한 예제와 더 기술적인 글을 다음에 하겠습니다.

렌더스크립트는 고성능 3D 렌더링과 컴퓨팅 연산에 맞춘 새 API입니다. 렌더 스크립트의 목적은 저수준에서 얻은 고성능의 API를 안드로이드 개발자에게 주는 겁니다. 자신의 애플리케이션 성능을 최적화려 기계 수준까지 작업하는게 익숙한 개발자를 대상으로 합니다. 렌더 스크립트는 개발자에게 3종의 기본 도구를 제공합니다. 하드웨어 가속되는 간단한 3D 렌더링 API, CUDA와 유사한 개발자 친화적인 연산 API, C99와 흡사한 언어입니다.

렌더스크립트는 유튜브와 책 앱을 화려하게 했습니다. 허니컴 타블렛에 탑재된 첫 라이브 배경화면도 이 API를 이용했죠.

디바이스에서 네이티브 코드를 수행함으로서 성능향상을 얻습니다. 기존의 NDK와는 달리 이 해법은 플랫폼을 가리지 않습니다. 렌더스크립트 용 개발언어는 C99를 확장한 언어입니다. 이 언어는 개발할 땐 장비와 무관하게 컴파일되어 애플리케이션 패키징됩니다. 앱이 수행될 때 이 스크립트를 기계 코드로 컴파일하여 장비에 최적화합니다. 개발 중에 특정 머신의 아키텍쳐로 고정하는 문제를 제거한 거죠.

렌더스크립트는 기존 플랫폼의 고수준 렌더링 API와 언어를 대체하는 것이 목표가 아닙니다. 기존 API의 능력 밖의 성능이 필요한 중요 코드에 쓰려는 겁니다.

CPU냐 GPU냐? 이런 대립적인 얘기를 하지 않는것이 흥미로울 지도 모릅니다. 장비에서 실시간으로 수행될 곳이 정해지기 때문에 어디에서 수행되는 것인지는 의미가 없습니다. 간단한 스크립트는 작업을 GPU에서 연산하죠. 더 복잡한 스크립트는 CPU에서 하고요. CPU는 적합한 GPU와 다른 가속기가 없는 상황에서도 언제나 제대로 동작하게 해주는 대체제이기도 합니다. 그래서 렌더스크립트는 개발자들에게 투명합니다. 일반적으로 작은 스크립트는 이 후에 더 다양한 영역에서 수행될 수 있어요. 지금 당장은 단순히 CPU를 더 꽂아 CPU 리소스를 더 활용할 수 있죠.



위 비디오는 렌더스크립트 예제를 동작시켜 타블렛의 HDMI out 출력을 캡쳐한 것입니다. (고화질 버전은 유튜브에 있습니다.) 이 비디오는 단순 무식하게 물리 시물레이션 중인 900개의 입자를 볼 수 있습니다. 이 계산 스크립트는 매 프레임 동작하며 자동으로 양쪽 코어 (CPU, GPU)를 활용합니다. 물리 시물레이션이 끝나면 이어 그래픽 스크립트가 렌더링을 합니다. 비디오에선 하나의 큰 공을 집어넣어 상호 작용시켰습니다. 그리고 타블렛을 기울여서 중력을 활용했죠. 신형 허니컴 타블렛의 듀얼 코어 A9의 위력을 보여줍니다.

렌더스크립트 그래픽은 연속적인 장면을 랜더링하기 위한 새로운 런타임을 제공합니다. 이 런타임은 하드웨어 가속되며, 사용자의 스크립트를 이용하여 통제된 달빅 코드에 대한 추가된 기능을 제공합니다. 이 통제된 코드는 "페이지를 켜"나 "리스트를 움직여" 같은 다소 정제 덜 된 명령을 보냅니다. 이 두 측면의 명령어는 사용자 스크립트에 의해 결정됩니다. 이런 방식으로 전체적으로 원하는 방식대로 정할 수 있죠. 렌더스크립트 그래픽의 이전 예제는 이클레어에 실린 라이브 배경화면과 3D 애플리케이션 런쳐입니다.

허니컴에선 렌더스크립트가 렌더러를 GL ES 1.1에서 2.0으로 바꾸었습니다. 동시에 프로그램 가능한 세이더 모델, 3D 모델 로딩, 더 많이 효율적인 할당 관리 등을 추가하였습니다. LLVM 기반의 새 컴파일러는 이클레어, 진저브레이드의 타임 프레임에 사용된 acc보다 몇배는 효과적입니다. 렌더스크립트 API와 도구의 가장 중요한 변화는 이제 공식이라는 것입니다.


위의 스크린 샷은 내부 테스트 앱에서 찍은 것입니다. 애플리케이션은 간단한 장면 그래프(scene-graph)를 구현한 것이고요. 재귀적으로 스크립트가 스크립트를 호출합니다. 이 안드로이드는 마야로 작성된 콜라라도 파일을 A3D파일로 변형해 읽은 것입니다. A3D 파일은 장비에서 렌더스크립트 객체를 저장하는데 사용하는 파일 형식입니다.

다음으로 더 기술적인 정보와 샘플 코드로 이어가겠습니다.

2011년 10월 23일 일요일

안드로이드용 음성 입력 (Speech Input) API

Alex Gruenstein에 의해 2010년 3월 5일 오전 10시에 작성되었습니다.
Speech Input API for Android의 번역입니다.

사람들은 휴대폰을 어디서나 만질 수 있어 좋아합니다. 대화 뿐만 아니라 이메일, 문자, 마이크로블로깅 등을 하고요. 게다가 안드로이드 2.1 버전에서는 음성 지원 키보드가  추가되었고 음성으로 검색하고 "~로 가자"와 같은 음성 단축키와 같은 기능도 쉽게 사용할 수 잇습니다. 이제 우리의 메시지를 치지 않고 말로 할 수 있습니다. 키보드에 새로 들어온 마이크로폰 버튼을 누른 후 평소에 타이핑하던 것들을 그냥 말하면 됩니다.

우리는 음성 (입력)이 모바일 경험을 근본적으로 바꿀 수 있다고 믿습니다. 우리는 안드로이드 SDK를 통해 음성 입력 능력을 통합하는 것을 고려하도록 모든 안드로이드 애플리케이션 개발자들을 초대하고 싶습니다. 내가 좋아하는 마켓에 올라온 음성 입력 가능한 앱은 Handcent SMS입니다. 어떤 SMS라도 SMS 팝업 윈도우가 뜨면 툭 건딜어 답장할 수 있죠.



음성 입력 통합된 Handcent SMS

안드로이드 SDK는 애플리케이션에 음성 입력을 직접 결합하는 것을 쉽게 합니다. 이 샘플 애플리케이션을 복사 붙여넣기 해서 시작점으로 삼으세요. 안드로이드는 개방된 플랫폼이기 때문에 디바이스에 RecognizerIntent를 받게 등록된 음성 인식 서비스를 아무거나 사용할 수 있습니다. 많은 안드로이드 단말에 미리 설치된 구글 음성 검색 애플리케이션은 RecognizerIntent를 받아 "Speak Now" 대화창을 표시하고 음성을 구글 서버로 전송합니다. 이 서버는 검색 위젯이나 음성 지원 키보드에서 마이크로폰 버튼을 눌렀을 때도 사용됩니다. (음성 검색이 설치되어있는지 여부는 Settings -> Applications -> Manage applications.에서 확인할 수 있습니다.)

중요한 팁 하나: 말해질 문장에 대해 생각하면 음성 입력을 더 정확하게 할 수 있습니다. "엄마, 내 목소리로 메시지를 보내는 거야."는 메일이나 SMS 메시지에 적합하고 구글 검색을 이용한다면 "마운틴 뷰의 날씨"등을 말이 더 맞을겁니다. 적합한 언어 모델을 요청하면사용자 경험을 향상할 수 있습니다. 긴 문장은 "free_form"을 쓰고 검색 문장과 같이 짧은 것은 "short_search"를 쓰는 거죠. 우리는 "free_from" 모델이 넥서스 원 보이스 키보드의 받아쓰기의 적합도를 향상하고 반면에 "web_search"는 사용자가 음성 검색을 원할때 사용하도록 개발했습니다.

구글 서버는 현재 영어, 만달린 중국어, 일본어를 지원합니다. 웹 검색(web_search) 모델은 세 언어 모두 지원하고 자유 형식 모델은 영어에만 우선 최적화되어 있습니다. 우리가 다른 언어의 다른 모델들을 열심히 개발하고  우리 제품에 사용될 음성 인식 기술의 적합도를 향상시키고 있습니다. 애플리케이션에 음성 능력을 바로 통합시킨 개발자들은 이익을 거두어가게 될 것입니다.