blob: 40873136ebe8654d1ac7734348914d8a5f5854fc [file] [log] [blame]
John Reckcec24ae2013-11-05 13:27:50 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import android.graphics.Rect;
20import android.graphics.SurfaceTexture;
21import android.os.Looper;
22import android.os.SystemClock;
23import android.os.Trace;
24import android.util.Log;
25import android.view.Surface.OutOfResourcesException;
26import android.view.View.AttachInfo;
27
28import java.io.PrintWriter;
29import java.lang.reflect.Method;
30import java.util.HashMap;
31
32/**
33 * Hardware renderer that proxies the rendering to a render thread. Most calls
34 * are synchronous, however a few such as draw() are posted async. The display list
35 * is shared between the two threads and is guarded by a top level lock.
36 *
37 * The UI thread can block on the RenderThread, but RenderThread must never
38 * block on the UI thread.
39 *
40 * Note that although currently the EGL context & surfaces are created & managed
41 * by the render thread, the goal is to move that into a shared structure that can
42 * be managed by both threads. EGLSurface creation & deletion should ideally be
43 * done on the UI thread and not the RenderThread to avoid stalling the
44 * RenderThread with surface buffer allocation.
45 *
46 * @hide
47 */
48public class ThreadedRenderer extends HardwareRenderer {
49 private static final String LOGTAG = "ThreadedRenderer";
50
51 @SuppressWarnings("serial")
52 static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{
53 Method[] methods = HardwareRenderer.class.getDeclaredMethods();
54 for (Method m : methods) {
55 put(m.getName(), m);
56 }
57 }};
58 static boolean sNeedsInit = true;
59
60 private HardwareRenderer mRemoteRenderer;
61 private int mWidth, mHeight;
62 private RTJob mPreviousDraw;
63
64 ThreadedRenderer(GLRenderer backingRenderer) {
65 mRemoteRenderer = backingRenderer;
66 setEnabled(true);
67 if (sNeedsInit) {
68 sNeedsInit = false;
69 postToRenderThread(new Runnable() {
70 @Override
71 public void run() {
72 // Hack to allow GLRenderer to create a handler to post the EGL
73 // destruction to, although it'll never run
74 Looper.prepare();
75 }
76 });
77 }
78 }
79
80 @Override
81 void destroy(boolean full) {
82 run("destroy", full);
83 }
84
85 @Override
86 boolean initialize(Surface surface) throws OutOfResourcesException {
87 return (Boolean) run("initialize", surface);
88 }
89
90 @Override
91 void updateSurface(Surface surface) throws OutOfResourcesException {
92 post("updateSurface", surface);
93 }
94
95 @Override
96 void destroyLayers(View view) {
97 throw new NoSuchMethodError();
98 }
99
100 @Override
101 void destroyHardwareResources(View view) {
102 run("destroyHardwareResources", view);
103 }
104
105 @Override
106 void invalidate(Surface surface) {
107 post("invalidate", surface);
108 }
109
110 @Override
111 boolean validate() {
112 // TODO Remove users of this API
113 return false;
114 }
115
116 @Override
117 boolean safelyRun(Runnable action) {
118 return (Boolean) run("safelyRun", action);
119 }
120
121 @Override
122 void setup(int width, int height) {
123 mWidth = width;
124 mHeight = height;
125 post("setup", width, height);
126 }
127
128 @Override
129 int getWidth() {
130 return mWidth;
131 }
132
133 @Override
134 int getHeight() {
135 return mHeight;
136 }
137
138 @Override
139 void dumpGfxInfo(PrintWriter pw) {
140 // TODO Auto-generated method stub
141 }
142
143 @Override
144 long getFrameCount() {
145 // TODO Auto-generated method stub
146 return 0;
147 }
148
149 @Override
150 boolean loadSystemProperties() {
151 return (Boolean) run("loadSystemProperties");
152 }
153
154 @Override
155 void pushLayerUpdate(HardwareLayer layer) {
156 throw new NoSuchMethodError();
157 }
158
159 @Override
160 void cancelLayerUpdate(HardwareLayer layer) {
161 throw new NoSuchMethodError();
162 }
163
164 @Override
165 void flushLayerUpdates() {
166 throw new NoSuchMethodError();
167 }
168
169 @Override
170 void drawDisplayList(DisplayList displayList, AttachInfo attachInfo,
171 HardwareDrawCallbacks callbacks, Rect dirty) {
172 throw new NoSuchMethodError();
173 }
174
175 /**
176 * TODO: Remove
177 * Temporary hack to allow RenderThreadTest prototype app to trigger
178 * replaying a DisplayList after modifying the displaylist properties
179 *
180 * @hide */
181 public void repeatLastDraw() {
182 if (mPreviousDraw == null) {
183 throw new IllegalStateException("There isn't a previous draw");
184 }
185 synchronized (mPreviousDraw) {
186 mPreviousDraw.completed = false;
187 }
188 mPreviousDraw.args[3] = null;
189 postToRenderThread(mPreviousDraw);
190 }
191
192 @Override
193 void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
194 requireCompletion(mPreviousDraw);
195
196 attachInfo.mIgnoreDirtyState = true;
197 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
198 view.mPrivateFlags |= View.PFLAG_DRAWN;
199
200 view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
201 == View.PFLAG_INVALIDATED;
202 view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
203
204 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
205 DisplayList displayList = view.getDisplayList();
206 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
207
208 view.mRecreateDisplayList = false;
209
210 mPreviousDraw = post("drawDisplayList", displayList, attachInfo,
211 callbacks, dirty);
212 }
213
214 @Override
215 HardwareLayer createHardwareLayer(boolean isOpaque) {
216 throw new NoSuchMethodError();
217 }
218
219 @Override
220 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
221 throw new NoSuchMethodError();
222 }
223
224 @Override
225 SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
226 throw new NoSuchMethodError();
227 }
228
229 @Override
230 void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
231 throw new NoSuchMethodError();
232 }
233
234 @Override
235 void detachFunctor(int functor) {
236 throw new NoSuchMethodError();
237 }
238
239 @Override
240 boolean attachFunctor(AttachInfo attachInfo, int functor) {
241 throw new NoSuchMethodError();
242 }
243
244 @Override
245 void setName(String name) {
246 post("setName", name);
247 }
248
249 private static void requireCompletion(RTJob job) {
250 if (job != null) {
251 synchronized (job) {
252 if (!job.completed) {
253 try {
254 job.wait();
255 } catch (InterruptedException e) {
256 throw new RuntimeException(e);
257 }
258 }
259 }
260 }
261 }
262
263 private RTJob post(String method, Object... args) {
264 RTJob job = new RTJob();
265 job.method = sMethodLut.get(method);
266 job.args = args;
267 job.target = mRemoteRenderer;
268 if (job.method == null) {
269 throw new NullPointerException("Couldn't find method: " + method);
270 }
271 postToRenderThread(job);
272 return job;
273 }
274
275 private Object run(String method, Object... args) {
276 RTJob job = new RTJob();
277 job.method = sMethodLut.get(method);
278 job.args = args;
279 job.target = mRemoteRenderer;
280 if (job.method == null) {
281 throw new NullPointerException("Couldn't find method: " + method);
282 }
283 synchronized (job) {
284 postToRenderThread(job);
285 try {
286 job.wait();
287 return job.ret;
288 } catch (InterruptedException e) {
289 throw new RuntimeException(e);
290 }
291 }
292 }
293
294 static class RTJob implements Runnable {
295 Method method;
296 Object[] args;
297 Object target;
298 Object ret;
299 boolean completed = false;
300
301 @Override
302 public void run() {
303 try {
304 ret = method.invoke(target, args);
305 synchronized (this) {
306 completed = true;
307 notify();
308 }
309 } catch (Exception e) {
310 Log.e(LOGTAG, "Failed to invoke: " + method.getName(), e);
311 }
312 }
313 }
314
315 /** @hide */
316 public static native void postToRenderThread(Runnable runnable);
317}