일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- android
- LRU
- MFC
- darkmode
- FirebaseAuth
- 보늬밤
- Java
- WebView
- DataStructure
- 리틀포레스트
- 제곱근
- 피보나치
- Kotlin
- stack
- Dialog
- 피요모리2
- synergy
- math
- 형변환
- devicedriver
- SPI
- Dokka
- QoS
- memory
- 쌓기게임
- AfxMessageBox
- 코인거스름돈
- 동적프로그래밍
- Collection
- DynamicProgramming
- Today
- Total
퉁탕퉁탕 만들어보자
TextureView를 써야할까 SurfaceView를 써야할까? 본문
Android Graphic Architecture를 (살짝) 알아보자
보통, 카메라 프리뷰나 MediaPlayer, OpenGL 을 사용할때 SurfaceView나 TextureView 둘 중에 하나를 선택을 해서 렌더링을 하게 된다. 두 View 모두 Surface를 통해서 ImageStream을 그린다는 점은 같지만, 동작방식이 다르다.
총 Flow를 살펴보면
이미지스트림 생성자(MediaPlayer또는 CameraPreview) -> 서피스 -> 서피스 플링거 -> HW Composer -> display
의 과정을 거친다.
먼저 이 모든 과정에서 사용되는 Buffer Queue에 대해서 알아보자
버퍼 큐는 여러개의 버퍼를 갖고있는 queue 이다. 보통 1~3개의 버퍼를 갖고있고, 각각 다른 option을 갖고있다.
우리가 buffer Queue를 setup할 때 얼마나 많은 buffer를 가질 지 request할 수 있다.
그리고 buffer queue는 2개의 end point가 있다.
* producer : 버퍼를 채워주는 쪽이고,
* consumer : 버퍼에 있는 값을 가져가는 쪽이다.
먼저 producer에서 BufferQueue의 dequeue buffer 를 한다. 그러면 queue에서 버퍼를 하나 잡아서, 여러가지 일들 미디어 플레이어,, OpenGL 등등 을 통해서 content을 채운다. 그다음 다 채웠으면 queue buffer를 콜해서 queue에 buffer를 넣는다.
그다음 이 채워진 Buffer 0 를 consumer쪽에서 BufferQueue에 acquireBuffer를 통해서 가져온다.
그다음 buffer의 내용물을 다 사용하고,
전부 사용하고난 뒤, release를 통해서 buffer를 BufferQueue에 돌려놓는다.
Window를 만드는 과정을 살펴보자
이 시나리오에서는 Window manager가 producer이고 SurfaceFlinger가 consumer이다.
WindowManger은 window들을 갖고있고, Widow는 Surface를 갖고있는데, Surface는 BufferQueue에게 보내주는 Producer이다. 우측 SurfaceFlinger가 합성하는 Layer는 하나의 BufferQueue를 가진 component이다. Layer는 버퍼큐를 생성하고, app에게 endpoint를 제공한다.
SurfaceView
SurfaceView의 경우, Window가 자기의 Surface를 갖고 있는데, 이 Surface에 구멍을 뚫는다.
그리고 WindowManager과 SurfaceFlinger에게 두번째 Surface를 생성하라고 요청한다.(아래 쪽 서피스) 그리고 이 Surface 아래에 두번째 Surface를 밀어넣고, 실제로는 두개의 독립적인 버퍼 큐를 가지고 독립적으로 동작하는 Surface이지만 두개가 같은 window인것처럼 사용한다.
그리고 이제 mediaplayer나 openGl에서 아래쪽 Surface에 bufferQueue를 채워준다.
SurfaceView가 아닌 뷰는 각각의 surface에 대한 버퍼를 채워준 뒤에, 이 두 Surface를 합성해서 SurfaceFlinger가 그리게 된다. SurfaceView를 사용하면 구멍이 뚫려있기 때문에 렌더링할때 거치는 합성 단계를 한단계 스킵할수있다.
SurfaceView의 Lifecycle with Activity
SurfaceView의 생명주기 콜백으로는 surfaceCreated(), surfaceChanged(), surfaceDestoryed() 가 있다.
* Activity생성
onCreate-> onResume -> surfaceCreated -> surfaceChanged
* 백키
onPause -> surfaceDestroyed()
* orientation 변경
onPause -> surfaceDestoryed -> onCreate -> onResume -> surfaceCreated -> surfaceChanged
* 전원버튼 클릭시
onPause (surface가 destory되지 않는다)
surfaceCreated에서 기존에 mediaPlayer에 붙어있던 surface가 destory된 경우 mediaPlayer에 create된 surface를 다시 붙여주어야 까만화면없이 잘 재생을 할 수있다.
SurfaceTexture
SurfaceView대신 TextureView를 사용하게 되면 SurfaceTexture가 BufferQueue를 만들어준다. 이 버퍼큐는 앱이 소비자이고, 생성자 (openGL, mediaplayer, camera preview) 에서 서피스텍스쳐의 버퍼큐를 채워준다.
TextureView
TextureView는 우리가 앱단에서 사용하는 뷰인데, 아래와 같은 역할을 한다.
* SurfaceTexture 생성
* RenderThread가 consumer - HW acceleration 사용하는 ImageView의 한가지 정도로 취급
* SurfaceTexture를 query해서 producer와의 endpoint를 생성하기 위함
TextureView는 HW Accelerated window에서만 사용이 가능하고 SW로 렌더링 할때는 아무것도 그리지않음. surfaceView와 다르게 일반적인 뷰라고 생각하면 됨. 그래서 서피스뷰에서는 불가능한 view 자체에 알파값주기, clipping하기, 애니메이션 주기가 가능하다.
이처럼 일반 뷰처럼 앱 view hierarchy에 포함되기 때문에 아까 말했듯이 SurfaceView에 비해서 퍼포먼스가 떨어진다. 내부적으로 Surface에서 -> View로 content가 copy가 이루어진다. 그래서 대부분 노말한 경우에는 SurfaceView가 추천되는것이다.
TextureView와 함께 사용하는 SurfaceTextureListener는 4가지이다.
Summary
abstract void | onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) |
abstract boolean | onSurfaceTextureDestroyed(SurfaceTexture surface) |
abstract void | onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) |
abstract void | onSurfaceTextureUpdated(SurfaceTexture surface) |
onSurfaceTextureDestoryed에서 true를 리턴해주면 이제 더이상 렌더링을 진행하지 않는다. 하지만 false를 리턴하게되면 SurfaceTexture를 삭제하지 않고 계속 갖고있게 된다. 이러면 이제 client가 직접 release를 해줘야한다.
true를 리턴하는게 편하기는 한데, false로 하게되면 방향변경시 삭제 - 재생성을 거치지 않아도 되기 때문에 효율적이다.
이런 처리는 SurfaceView에서는 불가능하다.
SurfaceFlinger는 여러 소스의 버퍼들을 합성해서 HW Composer에 넘겨주고, HWC에서 Display로 넘겨주며 화면이 그려진다.
정리
* TextureView는 일반뷰처럼 사용가능- 회전처리 뛰어남, 알파사용가능, bitmap얻어오기가능
* SurfaceView는 UI Layer과 별도로 존재하는 동영상을 합성하는 경우 성능 및 전력소모에서 이득 (서피스 뷰 아래에 그리는건 안되는데, 위에 얹는거는 가능)
* TextureView로 렌더링하면 먼저 콘텐츠를 App내의 뷰 계층 구조로 합성 하고 그 다음 SurfaceFlinger에서 앱레이어 + 다른 레이어들과 합성 (네비바 등등)
다른 뷰가 TextureView 위에 배치되면 콘텐츠 업데이트로 인해 다른 뷰 요소가 다시 그려질 수 있음. 따라서 보이는 모든 픽셀이 두 번 합성됨.
(1. 앱에서 합성 -> 2. SurfaceFlinger에서 합성)
* SurfaceView는 App내의 뷰 계층구조로 합성하지않고 SurfaceFlinger에서 바로 합성. 그리고 앱의 다른 뷰 친구들은 HW Composer에서 합성해줌
* 한 화면에 여러개의 SurfaceView 사용시 2개의 레이어가 추가되기 때문에 HW 에서가능한 overlay 수가 제한되므로 추천되지않음.
기타:
코드를 짜다보면 나오는 SurfaceHolder는 앱단에서 Surface를 가져오고 설정하고 하는데 사용되는 interface역할이다.
출처:
https://www.youtube.com/watch?v=zdQRIYOST64
'Computer > Android' 카테고리의 다른 글
Java의 GC (0) | 2022.04.17 |
---|---|
dp와 pixel 무엇이 다른가! (feat. sp) (0) | 2022.04.17 |
Android 시스템 Setting의 값 읽기/쓰기 (0) | 2022.04.16 |
내 Activity에서 볼륨 변경을 어떻게 알 수 있을까?(System content observer) (0) | 2022.04.15 |
onSaveInstanceState 는 어떻게 사용하지? (0) | 2022.04.15 |