1 page.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 28 with View. All UI elements go through a complicated measurement and layout 29 process that fits them into a rectangular area, and all visible View objects are 30 rendered to a SurfaceFlinger-created Surface that was set up by the 31 WindowManager when the app was brought to the foreground. The app's UI thread 32 performs layout and rendering to a single buffer (regardless of the number of 33 Layouts 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 36 position and size, and fit other elements around it. When it comes time to 37 render, however, the contents are completely transparent; The View part of a 38 SurfaceView is just a see-through placeholder.</p> 39 40 <p>When the SurfaceView's View component is about to become visible, the 41 framework 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 43 that notifies you when the Surface creation finishes.) By default, the new 44 Surface is placed behind the app UI Surface, but the default Z-ordering can be 45 overridden to put the Surface on top.</p> 46 47 <p>Whatever you render onto this Surface will be composited by SurfaceFlinger, 48 not by the app. This is the real power of SurfaceView: The Surface you get can 49 be rendered by a separate thread or a separate process, isolated from any 50 rendering performed by the app UI, and the buffers go directly to 51 SurfaceFlinger. You can't totally ignore the UI thread—you still have to 52 coordinate with the Activity lifecycle and you may need to adjust something if 53 the size or position of the View changes—but you have a whole Surface all 54 to yourself. Blending with the app UI and other layers is handled by the 55 Hardware Composer.</p> 56 57 <p>The new Surface is the producer side of a BufferQueue, whose consumer is a 58 SurfaceFlinger layer. You can update the Surface with any mechanism that can 59 feed a BufferQueue, such as surface-supplied Canvas functions, attach an 60 EGLSurface and draw on it with GLES, or configure a MediaCodec video decoder to 61 write 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 66 following 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 81 is in the back, the app UI layer sits on top of that, followed by the status and 82 navigation bars that are above everything else.</li> 83 <li>The <strong>source crop</strong> values indicate the portion of the 84 Surface's buffer that SurfaceFlinger will display. The app UI was given a 85 Surface equal to the full size of the display (1080x1920), but as there is no 86 point rendering and compositing pixels that will be obscured by the status and 87 navigation bars, the source is cropped to a rectangle that starts 75 pixels from 88 the top and ends 144 pixels from the bottom. The status and navigation bars have 89 smaller Surfaces, and the source crop describes a rectangle that begins at the 90 top left (0,0) and spans their content.</li> 91 <li>The <strong>frame</strong> values specify the rectangle where pixels 92 appear on the display. For the app UI layer, the frame matches the source crop 93 because we are copying (or overlaying) a portion of a display-sized layer to the 94 same location in another display-sized layer. For the status and navigation 95 bars, the size of the frame rectangle is the same, but the position is adjusted 96 so 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 98 matches the video size, which SurfaceFlinger knows because the MediaCodec 99 decoder (the buffer producer) is dequeuing buffers that size. The frame 100 rectangle has a completely different size—984x738.</li> 101 </ul> 102 103 <p>SurfaceFlinger handles size differences by scaling the buffer contents to 104 fill the frame rectangle, upscaling or downscaling as needed. This particular 105 size was chosen because it has the same aspect ratio as the video (4:3), and is 106 as wide as possible given the constraints of the View layout (which includes 107 some 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 110 BufferQueue would reallocate buffers to the new size automatically, and 111 SurfaceFlinger would adjust the source crop. If the aspect ratio of the new 112 video is different, the app would need to force a re-layout of the View to match 113 it, which causes the WindowManager to tell SurfaceFlinger to update the frame 114 rectangle.</p> 115 116 <p>If you're rendering on the Surface through some other means (such as GLES), 117 you can set the Surface size using the <code>SurfaceHolder#setFixedSize()</code> 118 call. For example, you could configure a game to always render at 1280x720, 119 which would significantly reduce the number of pixels that must be touched to 120 fill the screen on a 2560x1440 tablet or 4K television. The display processor 121 handles the scaling. If you don't want to letter- or pillar-box your game, you 122 could adjust the game's aspect ratio by setting the size so that the narrow 123 dimension is 720 pixels but the long dimension is set to maintain the aspect 124 ratio of the physical display (e.g. 1152x720 to match a 2560x1600 display). 125 For an example of this approach, see Grafika's "Hardware scaler exerciser" 126 activity.</p> 127 128 <h2 id=glsurfaceview>GLSurfaceView</h2> 129 130 <p>The GLSurfaceView class provides helper classes for managing EGL contexts, 131 inter-thread communication, and interaction with the Activity lifecycle. That's 132 it. 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 135 EGL context there. The state is cleaned up automatically when the activity 136 pauses. Most apps won't need to know anything about EGL to use GLES with 137 GLSurfaceView.</p> 138 139 <p>In most cases, GLSurfaceView is very helpful and can make working with GLES 140 easier. In some situations, it can get in the way. Use it if it helps, don't 141 if 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 146 from a thread other than the main UI thread. This raises some questions about 147 the interaction between that thread and the Activity lifecycle.</p> 148 149 <p>For an Activity with a SurfaceView, there are two separate but interdependent 150 state 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 174 get 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 176 quickly 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>—no <code>surfaceDestroyed()</code>. The Surface 181 remains alive, and rendering can continue. You can even keep getting 182 Choreographer events if you continue to request them. If you have a lock 183 screen that forces a different orientation, your Activity may be restarted when 184 the device is unblanked; but if not, you can come out of screen-blank with the 185 same Surface you had before.</p> 186 187 <p>This raises a fundamental question when using a separate renderer thread with 188 SurfaceView: Should the lifespan of the thread be tied to that of the Surface or 189 the Activity? The answer depends on what you want to happen when the screen 190 goes blank: (1) start/stop the thread on Activity start/stop or (2) start/stop 191 the thread on Surface create/destroy.</p> 192 193 <p>Option 1 interacts well with the app lifecycle. We start the renderer thread 194 in <code>onResume()</code> and stop it in <code>onPause()</code>. It gets a bit 195 awkward when creating and configuring the thread because sometimes the Surface 196 will already exist and sometimes it won't (e.g. it's still alive after toggling 197 the screen with the power button). We have to wait for the surface to be 198 created before we do some initialization in the thread, but we can't simply do 199 it in the <code>surfaceCreated()</code> callback because that won't fire again 200 if the Surface didn't get recreated. So we need to query or cache the Surface 201 state, and forward it to the renderer thread.</p> 202 203 <p class="note"><strong>Note:</strong> Be careful when passing objects 204 between threads. It is best to pass the Surface or SurfaceHolder through a 205 Handler message (rather than just stuffing it into the thread) to avoid issues 206 on multi-core systems. For details, refer to 207 <a href="http://developer.android.com/training/articles/smp.html">Android 208 SMP Primer</a>.</p> 209 210 <p>Option 2 is appealing because the Surface and the renderer are logically 211 intertwined. We start the thread after the Surface has been created, which 212 avoids some inter-thread communication concerns, and Surface created/changed 213 messages are simply forwarded. We need to ensure rendering stops when the 214 screen goes blank and resumes when it un-blanks; this could be a simple matter 215 of 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 217 renderer thread is running. It may not be so trivial though—if we animate 218 based on elapsed time between frames, we could have a very large gap when the 219 next 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 225 configured and whether it's executing. A related concern is extracting state 226 from the thread when the Activity is killed (in <code>onPause()</code> or 227 <code>onSaveInstanceState()</code>); in such cases, Option 1 works best because 228 after the renderer thread has been joined its state can be accessed without 229 synchronization primitives.</p> 230