1page.title=SurfaceView and GLSurfaceView
2@jd:body
3
4<!--
5    Copyright 2014 The Android Open Source Project
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18-->
19<div id="qv-wrapper">
20  <div id="qv">
21    <h2>In this document</h2>
22    <ol id="auto-toc">
23    </ol>
24  </div>
25</div>
26
27<p>The Android app framework UI is based on a hierarchy of objects that start
28with View. All UI elements go through a complicated measurement and layout
29process that fits them into a rectangular area, and all visible View objects are
30rendered to a SurfaceFlinger-created Surface that was set up by the
31WindowManager when the app was brought to the foreground. The app's UI thread
32performs layout and rendering to a single buffer (regardless of the number of
33Layouts and Views and whether or not the Views are hardware-accelerated).</p>
34
35<p>A SurfaceView takes the same parameters as other views, so you can give it a
36position and size, and fit other elements around it. When it comes time to
37render, however, the contents are completely transparent; The View part of a
38SurfaceView is just a see-through placeholder.</p>
39
40<p>When the SurfaceView's View component is about to become visible, the
41framework asks the WindowManager to ask SurfaceFlinger to create a new Surface.
42(This doesn't happen synchronously, which is why you should provide a callback
43that notifies you when the Surface creation finishes.) By default, the new
44Surface is placed behind the app UI Surface, but the default Z-ordering can be
45overridden to put the Surface on top.</p>
46
47<p>Whatever you render onto this Surface will be composited by SurfaceFlinger,
48not by the app. This is the real power of SurfaceView: The Surface you get can
49be rendered by a separate thread or a separate process, isolated from any
50rendering performed by the app UI, and the buffers go directly to
51SurfaceFlinger. You can't totally ignore the UI thread&mdash;you still have to
52coordinate with the Activity lifecycle and you may need to adjust something if
53the size or position of the View changes&mdash;but you have a whole Surface all
54to yourself. Blending with the app UI and other layers is handled by the
55Hardware Composer.</p>
56
57<p>The new Surface is the producer side of a BufferQueue, whose consumer is a
58SurfaceFlinger layer. You can update the Surface with any mechanism that can
59feed a BufferQueue, such as surface-supplied Canvas functions, attach an
60EGLSurface and draw on it with GLES, or configure a MediaCodec video decoder to
61write to it.</p>
62
63<h2 id=composition>Composition and the Hardware Scaler</h2>
64
65<p>Let's take a closer look at <code>dumpsys SurfaceFlinger</code>. The
66following output was taken while playing a movie in Grafika's "Play video
67(SurfaceView)" activity on a Nexus 5 in portrait orientation; the video is QVGA
68(320x240):</p>
69<p><pre>
70    type    |          source crop              |           frame           name
71------------+-----------------------------------+--------------------------------
72        HWC | [    0.0,    0.0,  320.0,  240.0] | [   48,  411, 1032, 1149] SurfaceView
73        HWC | [    0.0,   75.0, 1080.0, 1776.0] | [    0,   75, 1080, 1776] com.android.grafika/com.android.grafika.PlayMovieSurfaceActivity
74        HWC | [    0.0,    0.0, 1080.0,   75.0] | [    0,    0, 1080,   75] StatusBar
75        HWC | [    0.0,    0.0, 1080.0,  144.0] | [    0, 1776, 1080, 1920] NavigationBar
76  FB TARGET | [    0.0,    0.0, 1080.0, 1920.0] | [    0,    0, 1080, 1920] HWC_FRAMEBUFFER_TARGET
77</pre></p>
78
79<ul>
80<li>The <strong>list order</strong> is back to front: the SurfaceView's Surface
81is in the back, the app UI layer sits on top of that, followed by the status and
82navigation bars that are above everything else.</li>
83<li>The <strong>source crop</strong> values indicate the portion of the
84Surface's buffer that SurfaceFlinger will display. The app UI was given a
85Surface equal to the full size of the display (1080x1920), but as there is no
86point rendering and compositing pixels that will be obscured by the status and
87navigation bars, the source is cropped to a rectangle that starts 75 pixels from
88the top and ends 144 pixels from the bottom. The status and navigation bars have
89smaller Surfaces, and the source crop describes a rectangle that begins at the
90top left (0,0) and spans their content.</li>
91<li>The <strong>frame</strong> values specify the rectangle where pixels
92appear on the display. For the app UI layer, the frame matches the source crop
93because we are copying (or overlaying) a portion of a display-sized layer to the
94same location in another display-sized layer. For the status and navigation
95bars, the size of the frame rectangle is the same, but the position is adjusted
96so the navigation bar appears at the bottom of the screen.</li>
97<li>The <strong>SurfaceView layer</strong> holds our video content. The source crop
98matches the video size, which SurfaceFlinger knows because the MediaCodec
99decoder (the buffer producer) is dequeuing buffers that size. The frame
100rectangle has a completely different size&mdash;984x738.</li>
101</ul>
102
103<p>SurfaceFlinger handles size differences by scaling the buffer contents to
104fill the frame rectangle, upscaling or downscaling as needed. This particular
105size was chosen because it has the same aspect ratio as the video (4:3), and is
106as wide as possible given the constraints of the View layout (which includes
107some padding at the edges of the screen for aesthetic reasons).</p>
108
109<p>If you started playing a different video on the same Surface, the underlying
110BufferQueue would reallocate buffers to the new size automatically, and
111SurfaceFlinger would adjust the source crop. If the aspect ratio of the new
112video is different, the app would need to force a re-layout of the View to match
113it, which causes the WindowManager to tell SurfaceFlinger to update the frame
114rectangle.</p>
115
116<p>If you're rendering on the Surface through some other means (such as GLES),
117you can set the Surface size using the <code>SurfaceHolder#setFixedSize()</code>
118call. For example, you could configure a game to always render at 1280x720,
119which would significantly reduce the number of pixels that must be touched to
120fill the screen on a 2560x1440 tablet or 4K television. The display processor
121handles the scaling. If you don't want to letter- or pillar-box your game, you
122could adjust the game's aspect ratio by setting the size so that the narrow
123dimension is 720 pixels but the long dimension is set to maintain the aspect
124ratio of the physical display (e.g. 1152x720 to match a 2560x1600 display).
125For an example of this approach, see Grafika's "Hardware scaler exerciser"
126activity.</p>
127
128<h2 id=glsurfaceview>GLSurfaceView</h2>
129
130<p>The GLSurfaceView class provides helper classes for managing EGL contexts,
131inter-thread communication, and interaction with the Activity lifecycle. That's
132it. You do not need to use a GLSurfaceView to use GLES.</p>
133
134<p>For example, GLSurfaceView creates a thread for rendering and configures an
135EGL context there. The state is cleaned up automatically when the activity
136pauses. Most apps won't need to know anything about EGL to use GLES with
137GLSurfaceView.</p>
138
139<p>In most cases, GLSurfaceView is very helpful and can make working with GLES
140easier. In some situations, it can get in the way. Use it if it helps, don't
141if it doesn't.</p>
142
143<h2 id=activity>SurfaceView and the Activity Lifecycle</h2>
144
145<p>When using a SurfaceView, it's considered good practice to render the Surface
146from a thread other than the main UI thread. This raises some questions about
147the interaction between that thread and the Activity lifecycle.</p>
148
149<p>For an Activity with a SurfaceView, there are two separate but interdependent
150state machines:</p>
151
152<ol>
153<li>Application onCreate/onResume/onPause</li>
154<li>Surface created/changed/destroyed</li>
155</ol>
156
157<p>When the Activity starts, you get callbacks in this order:</p>
158
159<ul>
160<li>onCreate</li>
161<li>onResume</li>
162<li>surfaceCreated</li>
163<li>surfaceChanged</li>
164</ul>
165
166<p>If you hit back you get:</p>
167
168<ul>
169<li>onPause</li>
170<li>surfaceDestroyed (called just before the Surface goes away)</li>
171</ul>
172
173<p>If you rotate the screen, the Activity is torn down and recreated and you
174get the full cycle. You can tell it's a quick restart by checking
175<code>isFinishing()</code>. It might be possible to start/stop an Activity so
176quickly that <code>surfaceCreated()</code> might actually happen after
177<code>onPause()</code>.</p>
178
179<p>If you tap the power button to blank the screen, you get only
180<code>onPause()</code>&mdash;no <code>surfaceDestroyed()</code>. The Surface
181remains alive, and rendering can continue. You can even keep getting
182Choreographer events if you continue to request them. If you have a lock
183screen that forces a different orientation, your Activity may be restarted when
184the device is unblanked; but if not, you can come out of screen-blank with the
185same Surface you had before.</p>
186
187<p>This raises a fundamental question when using a separate renderer thread with
188SurfaceView: Should the lifespan of the thread be tied to that of the Surface or
189the Activity? The answer depends on what you want to happen when the screen
190goes blank: (1) start/stop the thread on Activity start/stop or (2) start/stop
191the thread on Surface create/destroy.</p>
192
193<p>Option 1 interacts well with the app lifecycle. We start the renderer thread
194in <code>onResume()</code> and stop it in <code>onPause()</code>. It gets a bit
195awkward when creating and configuring the thread because sometimes the Surface
196will already exist and sometimes it won't (e.g. it's still alive after toggling
197the screen with the power button). We have to wait for the surface to be
198created before we do some initialization in the thread, but we can't simply do
199it in the <code>surfaceCreated()</code> callback because that won't fire again
200if the Surface didn't get recreated. So we need to query or cache the Surface
201state, and forward it to the renderer thread.</p>
202
203<p class="note"><strong>Note:</strong> Be careful when passing objects
204between threads. It is best to pass the Surface or SurfaceHolder through a
205Handler message (rather than just stuffing it into the thread) to avoid issues
206on multi-core systems. For details, refer to
207<a href="http://developer.android.com/training/articles/smp.html">Android
208SMP Primer</a>.</p>
209
210<p>Option 2 is appealing because the Surface and the renderer are logically
211intertwined. We start the thread after the Surface has been created, which
212avoids some inter-thread communication concerns, and Surface created/changed
213messages are simply forwarded. We need to ensure rendering stops when the
214screen goes blank and resumes when it un-blanks; this could be a simple matter
215of telling Choreographer to stop invoking the frame draw callback. Our
216<code>onResume()</code> will need to resume the callbacks if and only if the
217renderer thread is running. It may not be so trivial though&mdash;if we animate
218based on elapsed time between frames, we could have a very large gap when the
219next event arrives; an explicit pause/resume message may be desirable.</p>
220
221<p class="note"><strong>Note:</strong> For an example of Option 2, see Grafika's
222"Hardware scaler exerciser."</p>
223
224<p>Both options are primarily concerned with how the renderer thread is
225configured and whether it's executing. A related concern is extracting state
226from the thread when the Activity is killed (in <code>onPause()</code> or
227<code>onSaveInstanceState()</code>); in such cases, Option 1 works best because
228after the renderer thread has been joined its state can be accessed without
229synchronization primitives.</p>
230