blob: 289726bae1d9bdbc42d441f859713c890cebba9a [file] [log] [blame]
Jamie Gennis8ba32fa2010-12-20 11:27:26 -08001/*
2 * Copyright (C) 2010 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
17#define LOG_TAG "SurfaceTexture"
Jamie Gennise70d8b42011-01-09 13:24:09 -080018//#define LOG_NDEBUG 0
Jamie Gennis8ba32fa2010-12-20 11:27:26 -080019
20#define GL_GLEXT_PROTOTYPES
21#define EGL_EGLEXT_PROTOTYPES
22
23#include <EGL/egl.h>
24#include <EGL/eglext.h>
25#include <GLES2/gl2.h>
26#include <GLES2/gl2ext.h>
27
28#include <gui/SurfaceTexture.h>
29
Mathias Agopian7a042bf2011-04-11 21:19:55 -070030#include <hardware/hardware.h>
31
Mathias Agopian41f673c2011-11-17 17:48:35 -080032#include <private/gui/ComposerService.h>
33
Jamie Gennis8ba32fa2010-12-20 11:27:26 -080034#include <surfaceflinger/ISurfaceComposer.h>
35#include <surfaceflinger/SurfaceComposerClient.h>
Jamie Gennis9a78c902011-01-12 18:30:40 -080036#include <surfaceflinger/IGraphicBufferAlloc.h>
Jamie Gennis8ba32fa2010-12-20 11:27:26 -080037
38#include <utils/Log.h>
Mathias Agopian68c77942011-05-09 19:08:33 -070039#include <utils/String8.h>
Jamie Gennis8ba32fa2010-12-20 11:27:26 -080040
Mathias Agopian7c6eba62011-11-14 19:17:37 -080041#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
42#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true
43#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
44#else
45#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false
46#endif
Mathias Agopian29b57622011-08-17 15:42:04 -070047
Jamie Gennisfa28c352011-09-16 17:30:26 -070048// Macros for including the SurfaceTexture name in log messages
Steve Block6807e592011-10-20 11:56:00 +010049#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
Jamie Gennisfa28c352011-09-16 17:30:26 -070050#define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
51#define ST_LOGI(x, ...) LOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
52#define ST_LOGW(x, ...) LOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
53#define ST_LOGE(x, ...) LOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
Mathias Agopian29b57622011-08-17 15:42:04 -070054
Jamie Gennis8ba32fa2010-12-20 11:27:26 -080055namespace android {
56
Jamie Gennisf238e282011-01-09 16:33:17 -080057// Transform matrices
58static float mtxIdentity[16] = {
59 1, 0, 0, 0,
60 0, 1, 0, 0,
61 0, 0, 1, 0,
62 0, 0, 0, 1,
63};
64static float mtxFlipH[16] = {
65 -1, 0, 0, 0,
66 0, 1, 0, 0,
67 0, 0, 1, 0,
68 1, 0, 0, 1,
69};
70static float mtxFlipV[16] = {
71 1, 0, 0, 0,
72 0, -1, 0, 0,
73 0, 0, 1, 0,
74 0, 1, 0, 1,
75};
76static float mtxRot90[16] = {
77 0, 1, 0, 0,
78 -1, 0, 0, 0,
79 0, 0, 1, 0,
80 1, 0, 0, 1,
81};
82static float mtxRot180[16] = {
83 -1, 0, 0, 0,
84 0, -1, 0, 0,
85 0, 0, 1, 0,
86 1, 1, 0, 1,
87};
88static float mtxRot270[16] = {
89 0, -1, 0, 0,
90 1, 0, 0, 0,
91 0, 0, 1, 0,
92 0, 1, 0, 1,
93};
94
95static void mtxMul(float out[16], const float a[16], const float b[16]);
96
Jamie Gennisfa28c352011-09-16 17:30:26 -070097// Get an ID that's unique within this process.
98static int32_t createProcessUniqueId() {
99 static volatile int32_t globalCounter = 0;
100 return android_atomic_inc(&globalCounter);
101}
102
Jamie Gennisfb1b5a22011-09-28 12:13:31 -0700103SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
104 GLenum texTarget) :
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700105 mDefaultWidth(1),
106 mDefaultHeight(1),
107 mPixelFormat(PIXEL_FORMAT_RGBA_8888),
Mathias Agopian80727112011-05-02 19:51:12 -0700108 mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
109 mClientBufferCount(0),
110 mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
Eino-Ville Talvala1d01a122011-02-18 11:02:42 -0800111 mCurrentTexture(INVALID_BUFFER_SLOT),
112 mCurrentTransform(0),
113 mCurrentTimestamp(0),
Eino-Ville Talvala1d01a122011-02-18 11:02:42 -0800114 mNextTransform(0),
Mathias Agopian7734ebf2011-07-13 15:24:42 -0700115 mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700116 mTexName(tex),
Grace Kloba14a0e582011-06-23 21:21:47 -0700117 mSynchronousMode(false),
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700118 mAllowSynchronousMode(allowSynchronousMode),
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700119 mConnectedApi(NO_CONNECTED_API),
Jamie Gennisfb1b5a22011-09-28 12:13:31 -0700120 mAbandoned(false),
Sunita Nadampallia9297482011-11-09 18:23:41 -0600121 mTexTarget(texTarget),
122 mFrameCounter(0) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700123 // Choose a name using the PID and a process-unique ID.
124 mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
125
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700126 ST_LOGV("SurfaceTexture");
Jamie Gennis9a78c902011-01-12 18:30:40 -0800127 sp<ISurfaceComposer> composer(ComposerService::getComposerService());
128 mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
Mathias Agopiancc57d6f2011-04-27 18:57:33 -0700129 mNextCrop.makeInvalid();
Jamie Gennisfa28c352011-09-16 17:30:26 -0700130 memcpy(mCurrentTransformMatrix, mtxIdentity,
131 sizeof(mCurrentTransformMatrix));
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800132}
133
134SurfaceTexture::~SurfaceTexture() {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700135 ST_LOGV("~SurfaceTexture");
Mathias Agopianef51b992011-08-10 15:28:58 -0700136 freeAllBuffersLocked();
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800137}
138
Mathias Agopian80727112011-05-02 19:51:12 -0700139status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
140 if (bufferCount > NUM_BUFFER_SLOTS)
141 return BAD_VALUE;
142
143 // special-case, nothing to do
144 if (bufferCount == mBufferCount)
145 return OK;
146
147 if (!mClientBufferCount &&
148 bufferCount >= mBufferCount) {
149 // easy, we just have more buffers
150 mBufferCount = bufferCount;
151 mServerBufferCount = bufferCount;
152 mDequeueCondition.signal();
153 } else {
154 // we're here because we're either
155 // - reducing the number of available buffers
156 // - or there is a client-buffer-count in effect
157
158 // less than 2 buffers is never allowed
159 if (bufferCount < 2)
160 return BAD_VALUE;
161
162 // when there is non client-buffer-count in effect, the client is not
163 // allowed to dequeue more than one buffer at a time,
164 // so the next time they dequeue a buffer, we know that they don't
165 // own one. the actual resizing will happen during the next
166 // dequeueBuffer.
167
168 mServerBufferCount = bufferCount;
169 }
170 return OK;
171}
172
173status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
174 Mutex::Autolock lock(mMutex);
175 return setBufferCountServerLocked(bufferCount);
176}
177
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800178status_t SurfaceTexture::setBufferCount(int bufferCount) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700179 ST_LOGV("setBufferCount: count=%d", bufferCount);
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700180 Mutex::Autolock lock(mMutex);
Jamie Gennis9d4d6c12011-02-27 14:10:20 -0800181
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700182 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700183 ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700184 return NO_INIT;
185 }
Pannag Sanketi292a31a2011-06-24 09:56:27 -0700186 if (bufferCount > NUM_BUFFER_SLOTS) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700187 ST_LOGE("setBufferCount: bufferCount larger than slots available");
Pannag Sanketi292a31a2011-06-24 09:56:27 -0700188 return BAD_VALUE;
189 }
190
Mathias Agopian80727112011-05-02 19:51:12 -0700191 // Error out if the user has dequeued buffers
192 for (int i=0 ; i<mBufferCount ; i++) {
193 if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700194 ST_LOGE("setBufferCount: client owns some buffers");
Mathias Agopian80727112011-05-02 19:51:12 -0700195 return -EINVAL;
196 }
197 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700198
Jamie Gennis1c121f62011-07-30 16:00:11 -0700199 const int minBufferSlots = mSynchronousMode ?
200 MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
Mathias Agopian80727112011-05-02 19:51:12 -0700201 if (bufferCount == 0) {
Mathias Agopian80727112011-05-02 19:51:12 -0700202 mClientBufferCount = 0;
203 bufferCount = (mServerBufferCount >= minBufferSlots) ?
204 mServerBufferCount : minBufferSlots;
205 return setBufferCountServerLocked(bufferCount);
206 }
207
Jamie Gennis1c121f62011-07-30 16:00:11 -0700208 if (bufferCount < minBufferSlots) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700209 ST_LOGE("setBufferCount: requested buffer count (%d) is less than "
Jamie Gennis1c121f62011-07-30 16:00:11 -0700210 "minimum (%d)", bufferCount, minBufferSlots);
Jamie Gennis9d4d6c12011-02-27 14:10:20 -0800211 return BAD_VALUE;
212 }
213
Mathias Agopian80727112011-05-02 19:51:12 -0700214 // here we're guaranteed that the client doesn't have dequeued buffers
215 // and will release all of its buffer references.
Mathias Agopianef51b992011-08-10 15:28:58 -0700216 freeAllBuffersLocked();
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800217 mBufferCount = bufferCount;
Mathias Agopian80727112011-05-02 19:51:12 -0700218 mClientBufferCount = bufferCount;
Jamie Gennis67eedd72011-01-09 13:25:39 -0800219 mCurrentTexture = INVALID_BUFFER_SLOT;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700220 mQueue.clear();
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700221 mDequeueCondition.signal();
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800222 return OK;
223}
224
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700225status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
226{
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700227 ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
Mathias Agopian3fbce7c2011-07-25 19:56:08 -0700228 if (!w || !h) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700229 ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
230 w, h);
Mathias Agopian3fbce7c2011-07-25 19:56:08 -0700231 return BAD_VALUE;
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700232 }
Mathias Agopian3fbce7c2011-07-25 19:56:08 -0700233
234 Mutex::Autolock lock(mMutex);
235 mDefaultWidth = w;
236 mDefaultHeight = h;
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700237 return OK;
238}
239
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700240status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700241 ST_LOGV("requestBuffer: slot=%d", slot);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800242 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700243 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700244 ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700245 return NO_INIT;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800246 }
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700247 if (slot < 0 || mBufferCount <= slot) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700248 ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d",
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700249 mBufferCount, slot);
250 return BAD_VALUE;
251 }
252 mSlots[slot].mRequestBufferCalled = true;
253 *buf = mSlots[slot].mGraphicBuffer;
254 return NO_ERROR;
Mathias Agopianc04f1532011-04-25 20:22:14 -0700255}
256
257status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
258 uint32_t format, uint32_t usage) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700259 ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
Mathias Agopianc04f1532011-04-25 20:22:14 -0700260
Pannag Sanketi292a31a2011-06-24 09:56:27 -0700261 if ((w && !h) || (!w && h)) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700262 ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
Mathias Agopianc04f1532011-04-25 20:22:14 -0700263 return BAD_VALUE;
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700264 }
265
Mathias Agopianc04f1532011-04-25 20:22:14 -0700266 Mutex::Autolock lock(mMutex);
Mathias Agopian80727112011-05-02 19:51:12 -0700267
268 status_t returnFlags(OK);
269
Sunita Nadampallia9297482011-11-09 18:23:41 -0600270 int found = -1;
271 int foundSync = -1;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700272 int dequeuedCount = 0;
273 bool tryAgain = true;
274 while (tryAgain) {
Mathias Agopian2560d142011-08-10 16:33:23 -0700275 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700276 ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
Mathias Agopian2560d142011-08-10 16:33:23 -0700277 return NO_INIT;
278 }
Mathias Agopian2560d142011-08-10 16:33:23 -0700279
Mathias Agopian80727112011-05-02 19:51:12 -0700280 // We need to wait for the FIFO to drain if the number of buffer
281 // needs to change.
282 //
Mathias Agopian2560d142011-08-10 16:33:23 -0700283 // The condition "number of buffers needs to change" is true if
Mathias Agopian80727112011-05-02 19:51:12 -0700284 // - the client doesn't care about how many buffers there are
285 // - AND the actual number of buffer is different from what was
286 // set in the last setBufferCountServer()
287 // - OR -
288 // setBufferCountServer() was set to a value incompatible with
289 // the synchronization mode (for instance because the sync mode
290 // changed since)
291 //
292 // As long as this condition is true AND the FIFO is not empty, we
293 // wait on mDequeueCondition.
294
Mathias Agopian2560d142011-08-10 16:33:23 -0700295 const int minBufferCountNeeded = mSynchronousMode ?
Mathias Agopian80727112011-05-02 19:51:12 -0700296 MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
297
Mathias Agopian2560d142011-08-10 16:33:23 -0700298 const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
Mathias Agopian80727112011-05-02 19:51:12 -0700299 ((mServerBufferCount != mBufferCount) ||
Mathias Agopian2560d142011-08-10 16:33:23 -0700300 (mServerBufferCount < minBufferCountNeeded));
301
302 if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) {
Mathias Agopian80727112011-05-02 19:51:12 -0700303 // wait for the FIFO to drain
Mathias Agopian2560d142011-08-10 16:33:23 -0700304 mDequeueCondition.wait(mMutex);
305 // NOTE: we continue here because we need to reevaluate our
306 // whole state (eg: we could be abandoned or disconnected)
307 continue;
Mathias Agopian80727112011-05-02 19:51:12 -0700308 }
309
Mathias Agopian2560d142011-08-10 16:33:23 -0700310 if (numberOfBuffersNeedsToChange) {
Mathias Agopian80727112011-05-02 19:51:12 -0700311 // here we're guaranteed that mQueue is empty
Mathias Agopianef51b992011-08-10 15:28:58 -0700312 freeAllBuffersLocked();
Mathias Agopian80727112011-05-02 19:51:12 -0700313 mBufferCount = mServerBufferCount;
314 if (mBufferCount < minBufferCountNeeded)
315 mBufferCount = minBufferCountNeeded;
316 mCurrentTexture = INVALID_BUFFER_SLOT;
317 returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
318 }
319
320 // look for a free buffer to give to the client
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700321 found = INVALID_BUFFER_SLOT;
322 foundSync = INVALID_BUFFER_SLOT;
323 dequeuedCount = 0;
324 for (int i = 0; i < mBufferCount; i++) {
325 const int state = mSlots[i].mBufferState;
326 if (state == BufferSlot::DEQUEUED) {
327 dequeuedCount++;
328 }
Mathias Agopian29b57622011-08-17 15:42:04 -0700329
330 // if buffer is FREE it CANNOT be current
331 LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
332 "dequeueBuffer: buffer %d is both FREE and current!", i);
333
Mathias Agopian7c6eba62011-11-14 19:17:37 -0800334 if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
Mathias Agopian29b57622011-08-17 15:42:04 -0700335 if (state == BufferSlot::FREE || i == mCurrentTexture) {
336 foundSync = i;
337 if (i != mCurrentTexture) {
338 found = i;
339 break;
340 }
341 }
342 } else {
343 if (state == BufferSlot::FREE) {
Sunita Nadampallia9297482011-11-09 18:23:41 -0600344 /** For Asynchronous mode, we need to return the oldest of free buffers
345 * There is only one instance when the Framecounter overflows, this logic
346 * might return the earlier buffer to client. Which is a negligible impact
347 **/
348 if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
349 foundSync = i;
350 found = i;
351 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700352 }
353 }
354 }
Mathias Agopian80727112011-05-02 19:51:12 -0700355
356 // clients are not allowed to dequeue more than one buffer
357 // if they didn't set a buffer count.
358 if (!mClientBufferCount && dequeuedCount) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700359 ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
360 "setting the buffer count");
Mathias Agopian80727112011-05-02 19:51:12 -0700361 return -EINVAL;
362 }
363
Jamie Gennisc2c8dfd2011-05-23 18:44:04 -0700364 // See whether a buffer has been queued since the last setBufferCount so
365 // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below.
366 bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
367 if (bufferHasBeenQueued) {
368 // make sure the client is not trying to dequeue more buffers
369 // than allowed.
370 const int avail = mBufferCount - (dequeuedCount+1);
371 if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700372 ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded "
373 "(dequeued=%d)",
Jamie Gennisc2c8dfd2011-05-23 18:44:04 -0700374 MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
375 dequeuedCount);
376 return -EBUSY;
377 }
Mathias Agopian80727112011-05-02 19:51:12 -0700378 }
379
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700380 // we're in synchronous mode and didn't find a buffer, we need to wait
Mathias Agopian29b57622011-08-17 15:42:04 -0700381 // for some buffers to be consumed
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700382 tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
383 if (tryAgain) {
384 mDequeueCondition.wait(mMutex);
Mathias Agopianc04f1532011-04-25 20:22:14 -0700385 }
386 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700387
Mathias Agopian80727112011-05-02 19:51:12 -0700388 if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
389 // foundSync guaranteed to be != INVALID_BUFFER_SLOT
390 found = foundSync;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700391 }
392
Mathias Agopianc04f1532011-04-25 20:22:14 -0700393 if (found == INVALID_BUFFER_SLOT) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700394 // This should not happen.
395 ST_LOGE("dequeueBuffer: no available buffer slots");
Mathias Agopianc04f1532011-04-25 20:22:14 -0700396 return -EBUSY;
397 }
398
399 const int buf = found;
400 *outBuf = found;
401
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700402 const bool useDefaultSize = !w && !h;
403 if (useDefaultSize) {
404 // use the default size
405 w = mDefaultWidth;
406 h = mDefaultHeight;
407 }
408
409 const bool updateFormat = (format != 0);
410 if (!updateFormat) {
411 // keep the current (or default) format
412 format = mPixelFormat;
413 }
414
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700415 // buffer is now in DEQUEUED (but can also be current at the same time,
416 // if we're in synchronous mode)
417 mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
418
419 const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
Mathias Agopianc04f1532011-04-25 20:22:14 -0700420 if ((buffer == NULL) ||
421 (uint32_t(buffer->width) != w) ||
422 (uint32_t(buffer->height) != h) ||
423 (uint32_t(buffer->format) != format) ||
424 ((uint32_t(buffer->usage) & usage) != usage))
425 {
426 usage |= GraphicBuffer::USAGE_HW_TEXTURE;
Mathias Agopiand9e8c642011-07-01 14:53:49 -0700427 status_t error;
Mathias Agopianc04f1532011-04-25 20:22:14 -0700428 sp<GraphicBuffer> graphicBuffer(
Mathias Agopiand9e8c642011-07-01 14:53:49 -0700429 mGraphicBufferAlloc->createGraphicBuffer(
430 w, h, format, usage, &error));
Mathias Agopianc04f1532011-04-25 20:22:14 -0700431 if (graphicBuffer == 0) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700432 ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer "
433 "failed");
Mathias Agopiand9e8c642011-07-01 14:53:49 -0700434 return error;
Mathias Agopianc04f1532011-04-25 20:22:14 -0700435 }
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700436 if (updateFormat) {
437 mPixelFormat = format;
438 }
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800439 mSlots[buf].mGraphicBuffer = graphicBuffer;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700440 mSlots[buf].mRequestBufferCalled = false;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800441 if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
442 eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
443 mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
444 mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
445 }
Jamie Gennisaaa3ecf2011-11-17 16:00:44 -0800446 if (mCurrentTexture == buf) {
447 // The current texture no longer references the buffer in this slot
448 // since we just allocated a new buffer.
449 mCurrentTexture = INVALID_BUFFER_SLOT;
450 }
Mathias Agopian80727112011-05-02 19:51:12 -0700451 returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700452 }
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700453 ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
454 mSlots[buf].mGraphicBuffer->handle, returnFlags);
Mathias Agopian80727112011-05-02 19:51:12 -0700455 return returnFlags;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800456}
457
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700458status_t SurfaceTexture::setSynchronousMode(bool enabled) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700459 ST_LOGV("setSynchronousMode: enabled=%d", enabled);
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700460 Mutex::Autolock lock(mMutex);
Mathias Agopian80727112011-05-02 19:51:12 -0700461
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700462 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700463 ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700464 return NO_INIT;
465 }
466
Mathias Agopian80727112011-05-02 19:51:12 -0700467 status_t err = OK;
Grace Kloba14a0e582011-06-23 21:21:47 -0700468 if (!mAllowSynchronousMode && enabled)
469 return err;
470
Mathias Agopian80727112011-05-02 19:51:12 -0700471 if (!enabled) {
472 // going to asynchronous mode, drain the queue
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700473 err = drainQueueLocked();
474 if (err != NO_ERROR)
475 return err;
Mathias Agopian80727112011-05-02 19:51:12 -0700476 }
477
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700478 if (mSynchronousMode != enabled) {
Mathias Agopian80727112011-05-02 19:51:12 -0700479 // - if we're going to asynchronous mode, the queue is guaranteed to be
480 // empty here
481 // - if the client set the number of buffers, we're guaranteed that
482 // we have at least 3 (because we don't allow less)
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700483 mSynchronousMode = enabled;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700484 mDequeueCondition.signal();
485 }
Mathias Agopian80727112011-05-02 19:51:12 -0700486 return err;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700487}
488
Mathias Agopian97c602c2011-07-19 15:24:46 -0700489status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
490 uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700491 ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
Mathias Agopiancf46eb92011-05-11 15:05:29 -0700492
493 sp<FrameAvailableListener> listener;
494
495 { // scope for the lock
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700496 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700497 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700498 ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700499 return NO_INIT;
500 }
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700501 if (buf < 0 || buf >= mBufferCount) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700502 ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700503 mBufferCount, buf);
504 return -EINVAL;
505 } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700506 ST_LOGE("queueBuffer: slot %d is not owned by the client "
507 "(state=%d)", buf, mSlots[buf].mBufferState);
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700508 return -EINVAL;
509 } else if (buf == mCurrentTexture) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700510 ST_LOGE("queueBuffer: slot %d is current!", buf);
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700511 return -EINVAL;
512 } else if (!mSlots[buf].mRequestBufferCalled) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700513 ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700514 "buffer", buf);
515 return -EINVAL;
516 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700517
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700518 if (mSynchronousMode) {
Jamie Gennis3d8063b2011-06-26 18:27:47 -0700519 // In synchronous mode we queue all buffers in a FIFO.
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700520 mQueue.push_back(buf);
Jamie Gennis3d8063b2011-06-26 18:27:47 -0700521
522 // Synchronous mode always signals that an additional frame should
523 // be consumed.
524 listener = mFrameAvailableListener;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700525 } else {
Jamie Gennis3d8063b2011-06-26 18:27:47 -0700526 // In asynchronous mode we only keep the most recent buffer.
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700527 if (mQueue.empty()) {
528 mQueue.push_back(buf);
Jamie Gennis3d8063b2011-06-26 18:27:47 -0700529
530 // Asynchronous mode only signals that a frame should be
531 // consumed if no previous frame was pending. If a frame were
532 // pending then the consumer would have already been notified.
533 listener = mFrameAvailableListener;
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700534 } else {
535 Fifo::iterator front(mQueue.begin());
536 // buffer currently queued is freed
537 mSlots[*front].mBufferState = BufferSlot::FREE;
538 // and we record the new buffer index in the queued list
539 *front = buf;
540 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700541 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700542
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700543 mSlots[buf].mBufferState = BufferSlot::QUEUED;
544 mSlots[buf].mCrop = mNextCrop;
545 mSlots[buf].mTransform = mNextTransform;
Mathias Agopian7734ebf2011-07-13 15:24:42 -0700546 mSlots[buf].mScalingMode = mNextScalingMode;
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700547 mSlots[buf].mTimestamp = timestamp;
Sunita Nadampallia9297482011-11-09 18:23:41 -0600548 mFrameCounter++;
549 mSlots[buf].mFrameNumber = mFrameCounter;
550
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700551 mDequeueCondition.signal();
Mathias Agopian3902be62011-08-17 12:45:40 -0700552
553 *outWidth = mDefaultWidth;
554 *outHeight = mDefaultHeight;
555 *outTransform = 0;
Mathias Agopiancf46eb92011-05-11 15:05:29 -0700556 } // scope for the lock
557
558 // call back without lock held
559 if (listener != 0) {
560 listener->onFrameAvailable();
561 }
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800562 return OK;
563}
564
565void SurfaceTexture::cancelBuffer(int buf) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700566 ST_LOGV("cancelBuffer: slot=%d", buf);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800567 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700568
569 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700570 ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700571 return;
572 }
573
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700574 if (buf < 0 || buf >= mBufferCount) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700575 ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700576 mBufferCount, buf);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800577 return;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700578 } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700579 ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700580 buf, mSlots[buf].mBufferState);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800581 return;
582 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700583 mSlots[buf].mBufferState = BufferSlot::FREE;
Sunita Nadampallia9297482011-11-09 18:23:41 -0600584 mSlots[buf].mFrameNumber = 0;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700585 mDequeueCondition.signal();
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800586}
587
Jamie Gennisf238e282011-01-09 16:33:17 -0800588status_t SurfaceTexture::setCrop(const Rect& crop) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700589 ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
590 crop.bottom);
591
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800592 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700593 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700594 ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700595 return NO_INIT;
596 }
Jamie Gennisf238e282011-01-09 16:33:17 -0800597 mNextCrop = crop;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800598 return OK;
599}
600
601status_t SurfaceTexture::setTransform(uint32_t transform) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700602 ST_LOGV("setTransform: xform=%#x", transform);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800603 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700604 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700605 ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700606 return NO_INIT;
607 }
Jamie Gennisf238e282011-01-09 16:33:17 -0800608 mNextTransform = transform;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800609 return OK;
610}
611
Mathias Agopian5bfc2452011-08-08 19:14:03 -0700612status_t SurfaceTexture::connect(int api,
613 uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700614 ST_LOGV("connect: api=%d", api);
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700615 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700616
617 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700618 ST_LOGE("connect: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700619 return NO_INIT;
620 }
621
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700622 int err = NO_ERROR;
623 switch (api) {
624 case NATIVE_WINDOW_API_EGL:
625 case NATIVE_WINDOW_API_CPU:
626 case NATIVE_WINDOW_API_MEDIA:
627 case NATIVE_WINDOW_API_CAMERA:
628 if (mConnectedApi != NO_CONNECTED_API) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700629 ST_LOGE("connect: already connected (cur=%d, req=%d)",
Mathias Agopian8f9dbf92011-07-13 17:39:11 -0700630 mConnectedApi, api);
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700631 err = -EINVAL;
632 } else {
633 mConnectedApi = api;
Mathias Agopian5bfc2452011-08-08 19:14:03 -0700634 *outWidth = mDefaultWidth;
635 *outHeight = mDefaultHeight;
636 *outTransform = 0;
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700637 }
638 break;
639 default:
640 err = -EINVAL;
641 break;
642 }
643 return err;
644}
645
646status_t SurfaceTexture::disconnect(int api) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700647 ST_LOGV("disconnect: api=%d", api);
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700648 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700649
650 if (mAbandoned) {
Mathias Agopian8b8a0042011-11-18 14:30:20 -0800651 // it is not really an error to disconnect after the surface
652 // has been abandoned, it should just be a no-op.
653 return NO_ERROR;
Jamie Gennis7b305ff2011-07-19 12:08:33 -0700654 }
655
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700656 int err = NO_ERROR;
657 switch (api) {
658 case NATIVE_WINDOW_API_EGL:
659 case NATIVE_WINDOW_API_CPU:
660 case NATIVE_WINDOW_API_MEDIA:
661 case NATIVE_WINDOW_API_CAMERA:
662 if (mConnectedApi == api) {
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700663 drainQueueAndFreeBuffersLocked();
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700664 mConnectedApi = NO_CONNECTED_API;
Mathias Agopian70e3f812011-08-25 17:03:30 -0700665 mNextCrop.makeInvalid();
666 mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
667 mNextTransform = 0;
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700668 mDequeueCondition.signal();
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700669 } else {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700670 ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)",
Mathias Agopian8f9dbf92011-07-13 17:39:11 -0700671 mConnectedApi, api);
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700672 err = -EINVAL;
673 }
674 break;
675 default:
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700676 ST_LOGE("disconnect: unknown API %d", api);
Jamie Gennisfe0a87b2011-07-13 19:12:20 -0700677 err = -EINVAL;
678 break;
679 }
680 return err;
681}
682
Mathias Agopian7734ebf2011-07-13 15:24:42 -0700683status_t SurfaceTexture::setScalingMode(int mode) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700684 ST_LOGV("setScalingMode: mode=%d", mode);
Mathias Agopian7734ebf2011-07-13 15:24:42 -0700685
686 switch (mode) {
687 case NATIVE_WINDOW_SCALING_MODE_FREEZE:
688 case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
689 break;
690 default:
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700691 ST_LOGE("unknown scaling mode: %d", mode);
Mathias Agopian7734ebf2011-07-13 15:24:42 -0700692 return BAD_VALUE;
693 }
694
695 Mutex::Autolock lock(mMutex);
696 mNextScalingMode = mode;
697 return OK;
698}
699
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800700status_t SurfaceTexture::updateTexImage() {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700701 ST_LOGV("updateTexImage");
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800702 Mutex::Autolock lock(mMutex);
703
Mathias Agopiane47498f2011-08-08 19:35:15 -0700704 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700705 ST_LOGE("calling updateTexImage() on an abandoned SurfaceTexture");
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700706 return NO_INIT;
Mathias Agopiane47498f2011-08-08 19:35:15 -0700707 }
708
Jamie Gennis50c4efc2011-06-27 15:41:52 -0700709 // In asynchronous mode the list is guaranteed to be one buffer
710 // deep, while in synchronous mode we use the oldest buffer.
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700711 if (!mQueue.empty()) {
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700712 Fifo::iterator front(mQueue.begin());
Jamie Gennis50c4efc2011-06-27 15:41:52 -0700713 int buf = *front;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700714
Jamie Gennisf238e282011-01-09 16:33:17 -0800715 // Update the GL texture object.
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700716 EGLImageKHR image = mSlots[buf].mEglImage;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800717 if (image == EGL_NO_IMAGE_KHR) {
718 EGLDisplay dpy = eglGetCurrentDisplay();
Mathias Agopiane47498f2011-08-08 19:35:15 -0700719 if (mSlots[buf].mGraphicBuffer == 0) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700720 ST_LOGE("buffer at slot %d is null", buf);
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700721 return BAD_VALUE;
Mathias Agopiane47498f2011-08-08 19:35:15 -0700722 }
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700723 image = createImage(dpy, mSlots[buf].mGraphicBuffer);
724 mSlots[buf].mEglImage = image;
725 mSlots[buf].mEglDisplay = dpy;
Mathias Agopian3cd5a112011-04-26 14:57:40 -0700726 if (image == EGL_NO_IMAGE_KHR) {
727 // NOTE: if dpy was invalid, createImage() is guaranteed to
728 // fail. so we'd end up here.
729 return -EINVAL;
730 }
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800731 }
Jamie Gennis0eb88512011-01-26 11:52:02 -0800732
733 GLint error;
734 while ((error = glGetError()) != GL_NO_ERROR) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700735 ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
Jamie Gennis0eb88512011-01-26 11:52:02 -0800736 }
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700737
Jamie Gennisfb1b5a22011-09-28 12:13:31 -0700738 glBindTexture(mTexTarget, mTexName);
739 glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700740
Jamie Gennis0eb88512011-01-26 11:52:02 -0800741 bool failed = false;
742 while ((error = glGetError()) != GL_NO_ERROR) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700743 ST_LOGE("error binding external texture image %p (slot %d): %#04x",
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700744 image, buf, error);
Jamie Gennis0eb88512011-01-26 11:52:02 -0800745 failed = true;
746 }
747 if (failed) {
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800748 return -EINVAL;
749 }
Jamie Gennis9a78c902011-01-12 18:30:40 -0800750
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700751 ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
752 mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
753 mSlots[buf].mGraphicBuffer->handle);
754
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700755 if (mCurrentTexture != INVALID_BUFFER_SLOT) {
Jamie Gennis50c4efc2011-06-27 15:41:52 -0700756 // The current buffer becomes FREE if it was still in the queued
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700757 // state. If it has already been given to the client
758 // (synchronous mode), then it stays in DEQUEUED state.
759 if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED)
760 mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
761 }
762
Jamie Gennis9a78c902011-01-12 18:30:40 -0800763 // Update the SurfaceTexture state.
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700764 mCurrentTexture = buf;
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700765 mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700766 mCurrentCrop = mSlots[buf].mCrop;
767 mCurrentTransform = mSlots[buf].mTransform;
Mathias Agopian7734ebf2011-07-13 15:24:42 -0700768 mCurrentScalingMode = mSlots[buf].mScalingMode;
Jamie Gennis8cd5ba42011-05-19 13:33:00 -0700769 mCurrentTimestamp = mSlots[buf].mTimestamp;
Jamie Gennis736aa952011-06-12 17:03:06 -0700770 computeCurrentTransformMatrix();
Jamie Gennis50c4efc2011-06-27 15:41:52 -0700771
772 // Now that we've passed the point at which failures can happen,
773 // it's safe to remove the buffer from the front of the queue.
774 mQueue.erase(front);
Mathias Agopianb3e518c2011-04-21 18:52:51 -0700775 mDequeueCondition.signal();
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700776 } else {
777 // We always bind the texture even if we don't update its contents.
Jamie Gennisfb1b5a22011-09-28 12:13:31 -0700778 glBindTexture(mTexTarget, mTexName);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800779 }
Jamie Gennis50c4efc2011-06-27 15:41:52 -0700780
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800781 return OK;
782}
783
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700784bool SurfaceTexture::isExternalFormat(uint32_t format)
785{
786 switch (format) {
787 // supported YUV formats
788 case HAL_PIXEL_FORMAT_YV12:
789 // Legacy/deprecated YUV formats
790 case HAL_PIXEL_FORMAT_YCbCr_422_SP:
791 case HAL_PIXEL_FORMAT_YCrCb_420_SP:
792 case HAL_PIXEL_FORMAT_YCbCr_422_I:
793 return true;
794 }
795
796 // Any OEM format needs to be considered
797 if (format>=0x100 && format<=0x1FF)
798 return true;
799
800 return false;
801}
802
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700803GLenum SurfaceTexture::getCurrentTextureTarget() const {
Jamie Gennisfb1b5a22011-09-28 12:13:31 -0700804 return mTexTarget;
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700805}
806
Jamie Gennisf238e282011-01-09 16:33:17 -0800807void SurfaceTexture::getTransformMatrix(float mtx[16]) {
Jamie Gennisf238e282011-01-09 16:33:17 -0800808 Mutex::Autolock lock(mMutex);
Jamie Gennis736aa952011-06-12 17:03:06 -0700809 memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
810}
811
812void SurfaceTexture::computeCurrentTransformMatrix() {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700813 ST_LOGV("computeCurrentTransformMatrix");
Jamie Gennisf238e282011-01-09 16:33:17 -0800814
Jamie Gennisa214c642011-01-14 13:53:31 -0800815 float xform[16];
816 for (int i = 0; i < 16; i++) {
817 xform[i] = mtxIdentity[i];
818 }
819 if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
820 float result[16];
821 mtxMul(result, xform, mtxFlipH);
822 for (int i = 0; i < 16; i++) {
823 xform[i] = result[i];
824 }
825 }
826 if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
827 float result[16];
828 mtxMul(result, xform, mtxFlipV);
829 for (int i = 0; i < 16; i++) {
830 xform[i] = result[i];
831 }
832 }
833 if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
834 float result[16];
835 mtxMul(result, xform, mtxRot90);
836 for (int i = 0; i < 16; i++) {
837 xform[i] = result[i];
838 }
Jamie Gennisf238e282011-01-09 16:33:17 -0800839 }
840
841 sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
Jamie Gennisa214c642011-01-14 13:53:31 -0800842 float tx, ty, sx, sy;
843 if (!mCurrentCrop.isEmpty()) {
Jamie Gennisd99c0882011-03-10 16:24:46 -0800844 // In order to prevent bilinear sampling at the of the crop rectangle we
845 // may need to shrink it by 2 texels in each direction. Normally this
846 // would just need to take 1/2 a texel off each end, but because the
847 // chroma channels will likely be subsampled we need to chop off a whole
848 // texel. This will cause artifacts if someone does nearest sampling
849 // with 1:1 pixel:texel ratio, but it's impossible to simultaneously
850 // accomodate the bilinear and nearest sampling uses.
851 //
852 // If nearest sampling turns out to be a desirable usage of these
853 // textures then we could add the ability to switch a SurfaceTexture to
854 // nearest-mode. Preferably, however, the image producers (video
855 // decoder, camera, etc.) would simply not use a crop rectangle (or at
856 // least not tell the framework about it) so that the GPU can do the
857 // correct edge behavior.
858 int xshrink = 0, yshrink = 0;
859 if (mCurrentCrop.left > 0) {
860 tx = float(mCurrentCrop.left + 1) / float(buf->getWidth());
861 xshrink++;
862 } else {
863 tx = 0.0f;
864 }
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700865 if (mCurrentCrop.right < int32_t(buf->getWidth())) {
Jamie Gennisd99c0882011-03-10 16:24:46 -0800866 xshrink++;
867 }
Mathias Agopiana5c75c02011-03-31 19:10:24 -0700868 if (mCurrentCrop.bottom < int32_t(buf->getHeight())) {
Jamie Gennisd99c0882011-03-10 16:24:46 -0800869 ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
870 float(buf->getHeight());
871 yshrink++;
872 } else {
873 ty = 0.0f;
874 }
875 if (mCurrentCrop.top > 0) {
876 yshrink++;
877 }
878 sx = float(mCurrentCrop.width() - xshrink) / float(buf->getWidth());
879 sy = float(mCurrentCrop.height() - yshrink) / float(buf->getHeight());
Jamie Gennisa214c642011-01-14 13:53:31 -0800880 } else {
881 tx = 0.0f;
882 ty = 0.0f;
883 sx = 1.0f;
884 sy = 1.0f;
885 }
Jamie Gennisf238e282011-01-09 16:33:17 -0800886 float crop[16] = {
Jamie Gennisa214c642011-01-14 13:53:31 -0800887 sx, 0, 0, 0,
888 0, sy, 0, 0,
Jamie Gennisf238e282011-01-09 16:33:17 -0800889 0, 0, 1, 0,
Jamie Gennisd99c0882011-03-10 16:24:46 -0800890 tx, ty, 0, 1,
Jamie Gennisf238e282011-01-09 16:33:17 -0800891 };
892
Jamie Gennisa214c642011-01-14 13:53:31 -0800893 float mtxBeforeFlipV[16];
894 mtxMul(mtxBeforeFlipV, crop, xform);
895
896 // SurfaceFlinger expects the top of its window textures to be at a Y
897 // coordinate of 0, so SurfaceTexture must behave the same way. We don't
898 // want to expose this to applications, however, so we must add an
899 // additional vertical flip to the transform after all the other transforms.
Jamie Gennis736aa952011-06-12 17:03:06 -0700900 mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
Jamie Gennisf238e282011-01-09 16:33:17 -0800901}
902
Eino-Ville Talvala1d01a122011-02-18 11:02:42 -0800903nsecs_t SurfaceTexture::getTimestamp() {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700904 ST_LOGV("getTimestamp");
Eino-Ville Talvala1d01a122011-02-18 11:02:42 -0800905 Mutex::Autolock lock(mMutex);
906 return mCurrentTimestamp;
907}
908
Jamie Gennisc4d4aea2011-01-13 14:43:36 -0800909void SurfaceTexture::setFrameAvailableListener(
Pannag Sanketi292a31a2011-06-24 09:56:27 -0700910 const sp<FrameAvailableListener>& listener) {
Jamie Gennis6ee96ad2011-10-19 13:36:31 -0700911 ST_LOGV("setFrameAvailableListener");
Jamie Gennisc4d4aea2011-01-13 14:43:36 -0800912 Mutex::Autolock lock(mMutex);
Pannag Sanketi292a31a2011-06-24 09:56:27 -0700913 mFrameAvailableListener = listener;
Jamie Gennisc4d4aea2011-01-13 14:43:36 -0800914}
915
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700916void SurfaceTexture::freeBufferLocked(int i) {
917 mSlots[i].mGraphicBuffer = 0;
918 mSlots[i].mBufferState = BufferSlot::FREE;
Sunita Nadampallia9297482011-11-09 18:23:41 -0600919 mSlots[i].mFrameNumber = 0;
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700920 if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
921 eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
922 mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
923 mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
924 }
925}
926
Mathias Agopianef51b992011-08-10 15:28:58 -0700927void SurfaceTexture::freeAllBuffersLocked() {
928 LOGW_IF(!mQueue.isEmpty(),
929 "freeAllBuffersLocked called but mQueue is not empty");
Mathias Agopian29b57622011-08-17 15:42:04 -0700930 mCurrentTexture = INVALID_BUFFER_SLOT;
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800931 for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700932 freeBufferLocked(i);
933 }
934}
935
936void SurfaceTexture::freeAllBuffersExceptHeadLocked() {
937 LOGW_IF(!mQueue.isEmpty(),
938 "freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
939 int head = -1;
940 if (!mQueue.empty()) {
941 Fifo::iterator front(mQueue.begin());
942 head = *front;
943 }
Mathias Agopian29b57622011-08-17 15:42:04 -0700944 mCurrentTexture = INVALID_BUFFER_SLOT;
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700945 for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
946 if (i != head) {
947 freeBufferLocked(i);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800948 }
949 }
950}
951
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700952status_t SurfaceTexture::drainQueueLocked() {
Mathias Agopian2560d142011-08-10 16:33:23 -0700953 while (mSynchronousMode && !mQueue.isEmpty()) {
954 mDequeueCondition.wait(mMutex);
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700955 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700956 ST_LOGE("drainQueueLocked: SurfaceTexture has been abandoned!");
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700957 return NO_INIT;
958 }
959 if (mConnectedApi == NO_CONNECTED_API) {
Jamie Gennisfa28c352011-09-16 17:30:26 -0700960 ST_LOGE("drainQueueLocked: SurfaceTexture is not connected!");
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700961 return NO_INIT;
962 }
Mathias Agopian2560d142011-08-10 16:33:23 -0700963 }
Mathias Agopian8e19c2e2011-08-10 17:35:09 -0700964 return NO_ERROR;
965}
966
967status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() {
968 status_t err = drainQueueLocked();
969 if (err == NO_ERROR) {
970 if (mSynchronousMode) {
971 freeAllBuffersLocked();
972 } else {
973 freeAllBuffersExceptHeadLocked();
974 }
975 }
976 return err;
Mathias Agopian2560d142011-08-10 16:33:23 -0700977}
978
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800979EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
980 const sp<GraphicBuffer>& graphicBuffer) {
981 EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
982 EGLint attrs[] = {
983 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
984 EGL_NONE,
985 };
986 EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
987 EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
Mathias Agopian3cd5a112011-04-26 14:57:40 -0700988 if (image == EGL_NO_IMAGE_KHR) {
989 EGLint error = eglGetError();
Jamie Gennisfa28c352011-09-16 17:30:26 -0700990 ST_LOGE("error creating EGLImage: %#x", error);
Jamie Gennis8ba32fa2010-12-20 11:27:26 -0800991 }
992 return image;
993}
994
Mathias Agopian7a042bf2011-04-11 21:19:55 -0700995sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
996 Mutex::Autolock lock(mMutex);
997 return mCurrentTextureBuf;
998}
999
1000Rect SurfaceTexture::getCurrentCrop() const {
1001 Mutex::Autolock lock(mMutex);
1002 return mCurrentCrop;
1003}
1004
1005uint32_t SurfaceTexture::getCurrentTransform() const {
1006 Mutex::Autolock lock(mMutex);
1007 return mCurrentTransform;
1008}
1009
Mathias Agopian7734ebf2011-07-13 15:24:42 -07001010uint32_t SurfaceTexture::getCurrentScalingMode() const {
1011 Mutex::Autolock lock(mMutex);
1012 return mCurrentScalingMode;
1013}
1014
Jamie Gennis59769462011-11-19 18:04:43 -08001015bool SurfaceTexture::isSynchronousMode() const {
1016 Mutex::Autolock lock(mMutex);
1017 return mSynchronousMode;
1018}
1019
Mathias Agopianeafabcd2011-04-20 14:20:59 -07001020int SurfaceTexture::query(int what, int* outValue)
1021{
1022 Mutex::Autolock lock(mMutex);
Jamie Gennis7b305ff2011-07-19 12:08:33 -07001023
1024 if (mAbandoned) {
Jamie Gennisfa28c352011-09-16 17:30:26 -07001025 ST_LOGE("query: SurfaceTexture has been abandoned!");
Jamie Gennis7b305ff2011-07-19 12:08:33 -07001026 return NO_INIT;
1027 }
1028
Mathias Agopianeafabcd2011-04-20 14:20:59 -07001029 int value;
1030 switch (what) {
1031 case NATIVE_WINDOW_WIDTH:
1032 value = mDefaultWidth;
Mathias Agopianeafabcd2011-04-20 14:20:59 -07001033 break;
1034 case NATIVE_WINDOW_HEIGHT:
1035 value = mDefaultHeight;
Mathias Agopianeafabcd2011-04-20 14:20:59 -07001036 break;
1037 case NATIVE_WINDOW_FORMAT:
1038 value = mPixelFormat;
1039 break;
1040 case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
1041 value = mSynchronousMode ?
1042 (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
1043 break;
1044 default:
1045 return BAD_VALUE;
1046 }
1047 outValue[0] = value;
1048 return NO_ERROR;
1049}
Mathias Agopian7a042bf2011-04-11 21:19:55 -07001050
Jamie Gennis7b305ff2011-07-19 12:08:33 -07001051void SurfaceTexture::abandon() {
1052 Mutex::Autolock lock(mMutex);
Mathias Agopian8e19c2e2011-08-10 17:35:09 -07001053 mQueue.clear();
Jamie Gennis7b305ff2011-07-19 12:08:33 -07001054 mAbandoned = true;
Mathias Agopian97a98842011-08-03 15:18:36 -07001055 mCurrentTextureBuf.clear();
Mathias Agopian8e19c2e2011-08-10 17:35:09 -07001056 freeAllBuffersLocked();
Jamie Gennis7b305ff2011-07-19 12:08:33 -07001057 mDequeueCondition.signal();
1058}
1059
Jamie Gennisfa28c352011-09-16 17:30:26 -07001060void SurfaceTexture::setName(const String8& name) {
1061 mName = name;
1062}
1063
Mathias Agopian68c77942011-05-09 19:08:33 -07001064void SurfaceTexture::dump(String8& result) const
1065{
1066 char buffer[1024];
1067 dump(result, "", buffer, 1024);
1068}
1069
1070void SurfaceTexture::dump(String8& result, const char* prefix,
1071 char* buffer, size_t SIZE) const
1072{
1073 Mutex::Autolock _l(mMutex);
1074 snprintf(buffer, SIZE,
1075 "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
1076 "mPixelFormat=%d, mTexName=%d\n",
Jamie Gennisfa28c352011-09-16 17:30:26 -07001077 prefix, mBufferCount, mSynchronousMode, mDefaultWidth,
1078 mDefaultHeight, mPixelFormat, mTexName);
Mathias Agopian68c77942011-05-09 19:08:33 -07001079 result.append(buffer);
1080
1081 String8 fifo;
1082 int fifoSize = 0;
1083 Fifo::const_iterator i(mQueue.begin());
1084 while (i != mQueue.end()) {
1085 snprintf(buffer, SIZE, "%02d ", *i++);
1086 fifoSize++;
1087 fifo.append(buffer);
1088 }
1089
1090 snprintf(buffer, SIZE,
Jamie Gennis1f8e09f2011-07-19 17:58:43 -07001091 "%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n"
Mathias Agopian68c77942011-05-09 19:08:33 -07001092 "%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n"
1093 ,
1094 prefix, mCurrentCrop.left,
1095 mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
Jamie Gennis1f8e09f2011-07-19 17:58:43 -07001096 mCurrentTransform, mCurrentTexture,
Jamie Gennisfa28c352011-09-16 17:30:26 -07001097 prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right,
1098 mNextCrop.bottom, mNextTransform, fifoSize, fifo.string()
Mathias Agopian68c77942011-05-09 19:08:33 -07001099 );
1100 result.append(buffer);
1101
1102 struct {
1103 const char * operator()(int state) const {
1104 switch (state) {
1105 case BufferSlot::DEQUEUED: return "DEQUEUED";
1106 case BufferSlot::QUEUED: return "QUEUED";
1107 case BufferSlot::FREE: return "FREE";
1108 default: return "Unknown";
1109 }
1110 }
1111 } stateName;
1112
1113 for (int i=0 ; i<mBufferCount ; i++) {
1114 const BufferSlot& slot(mSlots[i]);
1115 snprintf(buffer, SIZE,
Mathias Agopianad795ba2011-08-08 16:02:13 -07001116 "%s%s[%02d] "
Mathias Agopianad795ba2011-08-08 16:02:13 -07001117 "state=%-8s, crop=[%d,%d,%d,%d], "
Mathias Agopian2db6f0a2011-08-09 15:48:43 -07001118 "transform=0x%02x, timestamp=%lld",
Mathias Agopianad795ba2011-08-08 16:02:13 -07001119 prefix, (i==mCurrentTexture)?">":" ", i,
Mathias Agopianad795ba2011-08-08 16:02:13 -07001120 stateName(slot.mBufferState),
Jamie Gennisfa28c352011-09-16 17:30:26 -07001121 slot.mCrop.left, slot.mCrop.top, slot.mCrop.right,
1122 slot.mCrop.bottom, slot.mTransform, slot.mTimestamp
Mathias Agopian68c77942011-05-09 19:08:33 -07001123 );
1124 result.append(buffer);
Mathias Agopian2db6f0a2011-08-09 15:48:43 -07001125
1126 const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
1127 if (buf != NULL) {
1128 snprintf(buffer, SIZE,
1129 ", %p [%4ux%4u:%4u,%3X]",
Jamie Gennisfa28c352011-09-16 17:30:26 -07001130 buf->handle, buf->width, buf->height, buf->stride,
1131 buf->format);
Mathias Agopian2db6f0a2011-08-09 15:48:43 -07001132 result.append(buffer);
1133 }
1134 result.append("\n");
Mathias Agopian68c77942011-05-09 19:08:33 -07001135 }
1136}
1137
Jamie Gennisf238e282011-01-09 16:33:17 -08001138static void mtxMul(float out[16], const float a[16], const float b[16]) {
1139 out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
1140 out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
1141 out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3];
1142 out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3];
1143
1144 out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7];
1145 out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7];
1146 out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7];
1147 out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7];
1148
1149 out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11];
1150 out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11];
1151 out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11];
1152 out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11];
1153
1154 out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15];
1155 out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15];
1156 out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15];
1157 out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];
1158}
1159
Jamie Gennis8ba32fa2010-12-20 11:27:26 -08001160}; // namespace android