|
1 /* |
|
2 * Copyright (C) 2010 The Android Open Source Project |
|
3 * Copyright (C) 2012-2013 Mozilla Foundation |
|
4 * |
|
5 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
6 * you may not use this file except in compliance with the License. |
|
7 * You may obtain a copy of the License at |
|
8 * |
|
9 * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 * |
|
11 * Unless required by applicable law or agreed to in writing, software |
|
12 * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 * See the License for the specific language governing permissions and |
|
15 * limitations under the License. |
|
16 */ |
|
17 |
|
18 #include "base/basictypes.h" |
|
19 #include "mozilla/layers/GrallocTextureClient.h" |
|
20 #include "mozilla/layers/ImageBridgeChild.h" |
|
21 #include "mozilla/layers/ShadowLayers.h" |
|
22 #include "mozilla/layers/ShadowLayerUtilsGralloc.h" |
|
23 #include "GonkNativeWindow.h" |
|
24 #include "nsDebug.h" |
|
25 |
|
26 /** |
|
27 * DOM_CAMERA_LOGI() is enabled in debug builds, and turned on by setting |
|
28 * NSPR_LOG_MODULES=Camera:N environment variable, where N >= 3. |
|
29 * |
|
30 * CNW_LOGE() is always enabled. |
|
31 */ |
|
32 #define CNW_LOGD(...) DOM_CAMERA_LOGI(__VA_ARGS__) |
|
33 #define CNW_LOGE(...) {(void)printf_stderr(__VA_ARGS__);} |
|
34 |
|
35 using namespace android; |
|
36 using namespace mozilla; |
|
37 using namespace mozilla::gfx; |
|
38 using namespace mozilla::layers; |
|
39 |
|
40 class nsProxyReleaseTask : public CancelableTask |
|
41 { |
|
42 public: |
|
43 nsProxyReleaseTask(TextureClient* aClient) |
|
44 : mTextureClient(aClient) { |
|
45 } |
|
46 |
|
47 virtual void Run() MOZ_OVERRIDE |
|
48 { |
|
49 mTextureClient = nullptr; |
|
50 } |
|
51 |
|
52 virtual void Cancel() MOZ_OVERRIDE {} |
|
53 |
|
54 private: |
|
55 mozilla::RefPtr<TextureClient> mTextureClient; |
|
56 }; |
|
57 |
|
58 GonkNativeWindow::GonkNativeWindow() : |
|
59 mAbandoned(false), |
|
60 mDefaultWidth(1), |
|
61 mDefaultHeight(1), |
|
62 mPixelFormat(PIXEL_FORMAT_RGBA_8888), |
|
63 mBufferCount(MIN_BUFFER_SLOTS + 1), |
|
64 mConnectedApi(NO_CONNECTED_API), |
|
65 mFrameCounter(0), |
|
66 mNewFrameCallback(nullptr) { |
|
67 } |
|
68 |
|
69 GonkNativeWindow::~GonkNativeWindow() { |
|
70 freeAllBuffersLocked(); |
|
71 } |
|
72 |
|
73 void GonkNativeWindow::abandon() |
|
74 { |
|
75 CNW_LOGD("abandon"); |
|
76 Mutex::Autolock lock(mMutex); |
|
77 mQueue.clear(); |
|
78 mAbandoned = true; |
|
79 freeAllBuffersLocked(); |
|
80 mDequeueCondition.signal(); |
|
81 } |
|
82 |
|
83 void GonkNativeWindow::freeAllBuffersLocked() |
|
84 { |
|
85 CNW_LOGD("freeAllBuffersLocked"); |
|
86 |
|
87 for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { |
|
88 if (mSlots[i].mGraphicBuffer != NULL) { |
|
89 if (mSlots[i].mTextureClient) { |
|
90 mSlots[i].mTextureClient->ClearRecycleCallback(); |
|
91 // release TextureClient in ImageBridge thread |
|
92 nsProxyReleaseTask* task = new nsProxyReleaseTask(mSlots[i].mTextureClient); |
|
93 mSlots[i].mTextureClient = NULL; |
|
94 ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); |
|
95 } |
|
96 mSlots[i].mGraphicBuffer = NULL; |
|
97 mSlots[i].mBufferState = BufferSlot::FREE; |
|
98 mSlots[i].mFrameNumber = 0; |
|
99 } |
|
100 } |
|
101 } |
|
102 |
|
103 void GonkNativeWindow::clearRenderingStateBuffersLocked() |
|
104 { |
|
105 CNW_LOGD("clearRenderingStateBuffersLocked"); |
|
106 |
|
107 for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { |
|
108 if (mSlots[i].mGraphicBuffer != NULL) { |
|
109 // Clear RENDERING state buffer |
|
110 if (mSlots[i].mBufferState == BufferSlot::RENDERING) { |
|
111 if (mSlots[i].mTextureClient) { |
|
112 mSlots[i].mTextureClient->ClearRecycleCallback(); |
|
113 // release TextureClient in ImageBridge thread |
|
114 nsProxyReleaseTask* task = new nsProxyReleaseTask(mSlots[i].mTextureClient); |
|
115 mSlots[i].mTextureClient = NULL; |
|
116 ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); |
|
117 } |
|
118 mSlots[i].mGraphicBuffer = NULL; |
|
119 mSlots[i].mBufferState = BufferSlot::FREE; |
|
120 mSlots[i].mFrameNumber = 0; |
|
121 } |
|
122 } |
|
123 } |
|
124 } |
|
125 |
|
126 status_t GonkNativeWindow::setBufferCount(int bufferCount) |
|
127 { |
|
128 CNW_LOGD("setBufferCount: count=%d", bufferCount); |
|
129 Mutex::Autolock lock(mMutex); |
|
130 |
|
131 if (mAbandoned) { |
|
132 CNW_LOGE("setBufferCount: GonkNativeWindow has been abandoned!"); |
|
133 return NO_INIT; |
|
134 } |
|
135 |
|
136 if (bufferCount > NUM_BUFFER_SLOTS) { |
|
137 CNW_LOGE("setBufferCount: bufferCount larger than slots available"); |
|
138 return BAD_VALUE; |
|
139 } |
|
140 |
|
141 if (bufferCount < MIN_BUFFER_SLOTS) { |
|
142 CNW_LOGE("setBufferCount: requested buffer count (%d) is less than " |
|
143 "minimum (%d)", bufferCount, MIN_BUFFER_SLOTS); |
|
144 return BAD_VALUE; |
|
145 } |
|
146 |
|
147 // Error out if the user has dequeued buffers. |
|
148 for (int i=0 ; i<mBufferCount ; i++) { |
|
149 if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { |
|
150 CNW_LOGE("setBufferCount: client owns some buffers"); |
|
151 return -EINVAL; |
|
152 } |
|
153 } |
|
154 |
|
155 if (bufferCount >= mBufferCount) { |
|
156 mBufferCount = bufferCount; |
|
157 //clear only buffers in RENDERING state. |
|
158 clearRenderingStateBuffersLocked(); |
|
159 mQueue.clear(); |
|
160 mDequeueCondition.signal(); |
|
161 return OK; |
|
162 } |
|
163 |
|
164 // here we're guaranteed that the client doesn't have dequeued buffers |
|
165 // and will release all of its buffer references. |
|
166 freeAllBuffersLocked(); |
|
167 mBufferCount = bufferCount; |
|
168 mQueue.clear(); |
|
169 mDequeueCondition.signal(); |
|
170 return OK; |
|
171 } |
|
172 |
|
173 status_t GonkNativeWindow::setDefaultBufferSize(uint32_t w, uint32_t h) |
|
174 { |
|
175 CNW_LOGD("setDefaultBufferSize: w=%d, h=%d", w, h); |
|
176 if (!w || !h) { |
|
177 CNW_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)", |
|
178 w, h); |
|
179 return BAD_VALUE; |
|
180 } |
|
181 |
|
182 Mutex::Autolock lock(mMutex); |
|
183 mDefaultWidth = w; |
|
184 mDefaultHeight = h; |
|
185 return OK; |
|
186 } |
|
187 |
|
188 status_t GonkNativeWindow::requestBuffer(int slot, sp<GraphicBuffer>* buf) |
|
189 { |
|
190 CNW_LOGD("requestBuffer: slot=%d", slot); |
|
191 Mutex::Autolock lock(mMutex); |
|
192 if (mAbandoned) { |
|
193 CNW_LOGE("requestBuffer: GonkNativeWindow has been abandoned!"); |
|
194 return NO_INIT; |
|
195 } |
|
196 if (slot < 0 || mBufferCount <= slot) { |
|
197 CNW_LOGE("requestBuffer: slot index out of range [0, %d]: %d", |
|
198 mBufferCount, slot); |
|
199 return BAD_VALUE; |
|
200 } |
|
201 mSlots[slot].mRequestBufferCalled = true; |
|
202 *buf = mSlots[slot].mGraphicBuffer; |
|
203 return NO_ERROR; |
|
204 } |
|
205 |
|
206 status_t GonkNativeWindow::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, |
|
207 uint32_t format, uint32_t usage) |
|
208 { |
|
209 if ((w && !h) || (!w && h)) { |
|
210 CNW_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); |
|
211 return BAD_VALUE; |
|
212 } |
|
213 |
|
214 status_t returnFlags(OK); |
|
215 bool updateFormat = false; |
|
216 bool alloc = false; |
|
217 int buf = INVALID_BUFFER_SLOT; |
|
218 |
|
219 { |
|
220 Mutex::Autolock lock(mMutex); |
|
221 |
|
222 int found = -1; |
|
223 int dequeuedCount = 0; |
|
224 int renderingCount = 0; |
|
225 bool tryAgain = true; |
|
226 |
|
227 CNW_LOGD("dequeueBuffer: E"); |
|
228 while (tryAgain) { |
|
229 if (mAbandoned) { |
|
230 CNW_LOGE("dequeueBuffer: GonkNativeWindow has been abandoned!"); |
|
231 return NO_INIT; |
|
232 } |
|
233 // look for a free buffer to give to the client |
|
234 found = INVALID_BUFFER_SLOT; |
|
235 dequeuedCount = 0; |
|
236 renderingCount = 0; |
|
237 for (int i = 0; i < mBufferCount; i++) { |
|
238 const int state = mSlots[i].mBufferState; |
|
239 switch (state) { |
|
240 case BufferSlot::DEQUEUED: |
|
241 CNW_LOGD("dequeueBuffer: slot %d is DEQUEUED\n", i); |
|
242 dequeuedCount++; |
|
243 break; |
|
244 |
|
245 case BufferSlot::RENDERING: |
|
246 CNW_LOGD("dequeueBuffer: slot %d is RENDERING\n", i); |
|
247 renderingCount++; |
|
248 break; |
|
249 |
|
250 case BufferSlot::FREE: |
|
251 CNW_LOGD("dequeueBuffer: slot %d is FREE\n", i); |
|
252 /* We return the oldest of the free buffers to avoid |
|
253 * stalling the producer if possible. This is because |
|
254 * the consumer may still have pending reads of the |
|
255 * buffers in flight. |
|
256 */ |
|
257 if (found < 0 || |
|
258 mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { |
|
259 found = i; |
|
260 } |
|
261 break; |
|
262 |
|
263 default: |
|
264 CNW_LOGD("dequeueBuffer: slot %d is %d\n", i, state); |
|
265 break; |
|
266 } |
|
267 } |
|
268 |
|
269 // See whether a buffer has been in RENDERING state since the last |
|
270 // setBufferCount so we know whether to perform the |
|
271 // MIN_UNDEQUEUED_BUFFERS check below. |
|
272 if (renderingCount > 0) { |
|
273 // make sure the client is not trying to dequeue more buffers |
|
274 // than allowed. |
|
275 const int avail = mBufferCount - (dequeuedCount + 1); |
|
276 if (avail < MIN_UNDEQUEUED_BUFFERS) { |
|
277 CNW_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " |
|
278 "(dequeued=%d)", |
|
279 MIN_UNDEQUEUED_BUFFERS, |
|
280 dequeuedCount); |
|
281 return -EBUSY; |
|
282 } |
|
283 } |
|
284 |
|
285 // we're in synchronous mode and didn't find a buffer, we need to |
|
286 // wait for some buffers to be consumed |
|
287 tryAgain = (found == INVALID_BUFFER_SLOT); |
|
288 if (tryAgain) { |
|
289 CNW_LOGD("dequeueBuffer: Try again"); |
|
290 mDequeueCondition.wait(mMutex); |
|
291 CNW_LOGD("dequeueBuffer: Now"); |
|
292 } |
|
293 } |
|
294 |
|
295 if (found == INVALID_BUFFER_SLOT) { |
|
296 // This should not happen. |
|
297 CNW_LOGE("dequeueBuffer: no available buffer slots"); |
|
298 return -EBUSY; |
|
299 } |
|
300 |
|
301 buf = found; |
|
302 *outBuf = found; |
|
303 |
|
304 const bool useDefaultSize = !w && !h; |
|
305 if (useDefaultSize) { |
|
306 // use the default size |
|
307 w = mDefaultWidth; |
|
308 h = mDefaultHeight; |
|
309 } |
|
310 |
|
311 updateFormat = (format != 0); |
|
312 if (!updateFormat) { |
|
313 // keep the current (or default) format |
|
314 format = mPixelFormat; |
|
315 } |
|
316 |
|
317 mSlots[buf].mBufferState = BufferSlot::DEQUEUED; |
|
318 |
|
319 const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer); |
|
320 if ((gbuf == NULL) || |
|
321 ((uint32_t(gbuf->width) != w) || |
|
322 (uint32_t(gbuf->height) != h) || |
|
323 (uint32_t(gbuf->format) != format) || |
|
324 ((uint32_t(gbuf->usage) & usage) != usage))) { |
|
325 mSlots[buf].mGraphicBuffer = NULL; |
|
326 mSlots[buf].mRequestBufferCalled = false; |
|
327 if (mSlots[buf].mTextureClient) { |
|
328 mSlots[buf].mTextureClient->ClearRecycleCallback(); |
|
329 // release TextureClient in ImageBridge thread |
|
330 nsProxyReleaseTask* task = new nsProxyReleaseTask(mSlots[buf].mTextureClient); |
|
331 mSlots[buf].mTextureClient = NULL; |
|
332 ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); |
|
333 } |
|
334 alloc = true; |
|
335 } |
|
336 } // end lock scope |
|
337 |
|
338 sp<GraphicBuffer> graphicBuffer; |
|
339 if (alloc) { |
|
340 RefPtr<GrallocTextureClientOGL> textureClient = |
|
341 new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), |
|
342 gfx::SurfaceFormat::UNKNOWN, |
|
343 gfx::BackendType::NONE, |
|
344 TEXTURE_DEALLOCATE_CLIENT); |
|
345 usage |= GraphicBuffer::USAGE_HW_TEXTURE; |
|
346 bool result = textureClient->AllocateGralloc(IntSize(w, h), format, usage); |
|
347 sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer(); |
|
348 if (!result || !graphicBuffer.get()) { |
|
349 CNW_LOGE("dequeueBuffer: failed to alloc gralloc buffer"); |
|
350 return -ENOMEM; |
|
351 } |
|
352 |
|
353 { // Scope for the lock |
|
354 Mutex::Autolock lock(mMutex); |
|
355 |
|
356 if (mAbandoned) { |
|
357 CNW_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); |
|
358 return NO_INIT; |
|
359 } |
|
360 |
|
361 if (updateFormat) { |
|
362 mPixelFormat = format; |
|
363 } |
|
364 mSlots[buf].mGraphicBuffer = graphicBuffer; |
|
365 mSlots[buf].mTextureClient = textureClient; |
|
366 |
|
367 returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; |
|
368 |
|
369 CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, |
|
370 mSlots[buf].mGraphicBuffer->handle); |
|
371 } |
|
372 } |
|
373 |
|
374 CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, |
|
375 mSlots[buf].mGraphicBuffer->handle ); |
|
376 |
|
377 CNW_LOGD("dequeueBuffer: X"); |
|
378 return returnFlags; |
|
379 } |
|
380 |
|
381 status_t GonkNativeWindow::setSynchronousMode(bool enabled) |
|
382 { |
|
383 return NO_ERROR; |
|
384 } |
|
385 |
|
386 int GonkNativeWindow::getSlotFromBufferLocked( |
|
387 android_native_buffer_t* buffer) const |
|
388 { |
|
389 if (buffer == NULL) { |
|
390 CNW_LOGE("getSlotFromBufferLocked: encountered NULL buffer"); |
|
391 return BAD_VALUE; |
|
392 } |
|
393 |
|
394 for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { |
|
395 if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) { |
|
396 return i; |
|
397 } |
|
398 } |
|
399 CNW_LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); |
|
400 return BAD_VALUE; |
|
401 } |
|
402 |
|
403 int GonkNativeWindow::getSlotFromTextureClientLocked( |
|
404 TextureClient* client) const |
|
405 { |
|
406 if (client == NULL) { |
|
407 CNW_LOGE("getSlotFromBufferLocked: encountered NULL buffer"); |
|
408 return BAD_VALUE; |
|
409 } |
|
410 |
|
411 for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { |
|
412 if (mSlots[i].mTextureClient == client) { |
|
413 return i; |
|
414 } |
|
415 } |
|
416 CNW_LOGE("getSlotFromBufferLocked: unknown TextureClient: %p", client); |
|
417 return BAD_VALUE; |
|
418 } |
|
419 |
|
420 TemporaryRef<TextureClient> |
|
421 GonkNativeWindow::getTextureClientFromBuffer(ANativeWindowBuffer* buffer) |
|
422 { |
|
423 int buf = getSlotFromBufferLocked(buffer); |
|
424 if (buf < 0 || buf >= mBufferCount || |
|
425 mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { |
|
426 return nullptr; |
|
427 } |
|
428 |
|
429 return mSlots[buf].mTextureClient; |
|
430 } |
|
431 |
|
432 status_t GonkNativeWindow::queueBuffer(int buf, int64_t timestamp, |
|
433 uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) |
|
434 { |
|
435 { |
|
436 Mutex::Autolock lock(mMutex); |
|
437 CNW_LOGD("queueBuffer: E"); |
|
438 CNW_LOGD("queueBuffer: buf=%d", buf); |
|
439 |
|
440 if (mAbandoned) { |
|
441 CNW_LOGE("queueBuffer: GonkNativeWindow has been abandoned!"); |
|
442 return NO_INIT; |
|
443 } |
|
444 if (buf < 0 || buf >= mBufferCount) { |
|
445 CNW_LOGE("queueBuffer: slot index out of range [0, %d]: %d", |
|
446 mBufferCount, buf); |
|
447 return -EINVAL; |
|
448 } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { |
|
449 CNW_LOGE("queueBuffer: slot %d is not owned by the client " |
|
450 "(state=%d)", buf, mSlots[buf].mBufferState); |
|
451 return -EINVAL; |
|
452 } else if (!mSlots[buf].mRequestBufferCalled) { |
|
453 CNW_LOGE("queueBuffer: slot %d was enqueued without requesting a " |
|
454 "buffer", buf); |
|
455 return -EINVAL; |
|
456 } |
|
457 |
|
458 mQueue.push_back(buf); |
|
459 |
|
460 mSlots[buf].mBufferState = BufferSlot::QUEUED; |
|
461 mSlots[buf].mTimestamp = timestamp; |
|
462 mFrameCounter++; |
|
463 mSlots[buf].mFrameNumber = mFrameCounter; |
|
464 |
|
465 mDequeueCondition.signal(); |
|
466 |
|
467 *outWidth = mDefaultWidth; |
|
468 *outHeight = mDefaultHeight; |
|
469 *outTransform = 0; |
|
470 } |
|
471 |
|
472 // OnNewFrame might call lockCurrentBuffer so we must release the |
|
473 // mutex first. |
|
474 if (mNewFrameCallback) { |
|
475 mNewFrameCallback->OnNewFrame(); |
|
476 } |
|
477 CNW_LOGD("queueBuffer: X"); |
|
478 return OK; |
|
479 } |
|
480 |
|
481 |
|
482 TemporaryRef<TextureClient> |
|
483 GonkNativeWindow::getCurrentBuffer() { |
|
484 CNW_LOGD("GonkNativeWindow::getCurrentBuffer"); |
|
485 Mutex::Autolock lock(mMutex); |
|
486 |
|
487 if (mAbandoned) { |
|
488 CNW_LOGE("getCurrentBuffer: GonkNativeWindow has been abandoned!"); |
|
489 return NULL; |
|
490 } |
|
491 |
|
492 if(mQueue.empty()) { |
|
493 mDequeueCondition.signal(); |
|
494 return nullptr; |
|
495 } |
|
496 |
|
497 Fifo::iterator front(mQueue.begin()); |
|
498 int buf = *front; |
|
499 CNW_LOGD("getCurrentBuffer: buf=%d", buf); |
|
500 |
|
501 mSlots[buf].mBufferState = BufferSlot::RENDERING; |
|
502 |
|
503 mQueue.erase(front); |
|
504 mDequeueCondition.signal(); |
|
505 |
|
506 mSlots[buf].mTextureClient->SetRecycleCallback(GonkNativeWindow::RecycleCallback, this); |
|
507 return mSlots[buf].mTextureClient; |
|
508 } |
|
509 |
|
510 |
|
511 /* static */ void |
|
512 GonkNativeWindow::RecycleCallback(TextureClient* client, void* closure) { |
|
513 GonkNativeWindow* nativeWindow = |
|
514 static_cast<GonkNativeWindow*>(closure); |
|
515 |
|
516 client->ClearRecycleCallback(); |
|
517 nativeWindow->returnBuffer(client); |
|
518 } |
|
519 |
|
520 void GonkNativeWindow::returnBuffer(TextureClient* client) { |
|
521 CNW_LOGD("GonkNativeWindow::returnBuffer"); |
|
522 Mutex::Autolock lock(mMutex); |
|
523 |
|
524 if (mAbandoned) { |
|
525 CNW_LOGD("returnBuffer: GonkNativeWindow has been abandoned!"); |
|
526 return; |
|
527 } |
|
528 |
|
529 int index = getSlotFromTextureClientLocked(client); |
|
530 if (index < 0 || index >= mBufferCount) { |
|
531 CNW_LOGE("returnBuffer: slot index out of range [0, %d]: %d", |
|
532 mBufferCount, index); |
|
533 return; |
|
534 } |
|
535 |
|
536 if (mSlots[index].mBufferState != BufferSlot::RENDERING) { |
|
537 CNW_LOGE("returnBuffer: slot %d is not owned by the compositor (state=%d)", |
|
538 index, mSlots[index].mBufferState); |
|
539 return; |
|
540 } |
|
541 |
|
542 mSlots[index].mBufferState = BufferSlot::FREE; |
|
543 mDequeueCondition.signal(); |
|
544 return; |
|
545 } |
|
546 |
|
547 void GonkNativeWindow::cancelBuffer(int buf) { |
|
548 CNW_LOGD("cancelBuffer: slot=%d", buf); |
|
549 Mutex::Autolock lock(mMutex); |
|
550 |
|
551 if (mAbandoned) { |
|
552 CNW_LOGD("cancelBuffer: GonkNativeWindow has been abandoned!"); |
|
553 return; |
|
554 } |
|
555 |
|
556 if (buf < 0 || buf >= mBufferCount) { |
|
557 CNW_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", |
|
558 mBufferCount, buf); |
|
559 return; |
|
560 } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { |
|
561 CNW_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", |
|
562 buf, mSlots[buf].mBufferState); |
|
563 return; |
|
564 } |
|
565 mSlots[buf].mBufferState = BufferSlot::FREE; |
|
566 mSlots[buf].mFrameNumber = 0; |
|
567 mDequeueCondition.signal(); |
|
568 } |
|
569 |
|
570 status_t GonkNativeWindow::setCrop(const Rect& crop) { |
|
571 return OK; |
|
572 } |
|
573 |
|
574 status_t GonkNativeWindow::setTransform(uint32_t transform) { |
|
575 return OK; |
|
576 } |
|
577 |
|
578 status_t GonkNativeWindow::connect(int api, |
|
579 uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { |
|
580 CNW_LOGD("connect: api=%d", api); |
|
581 Mutex::Autolock lock(mMutex); |
|
582 |
|
583 if (mAbandoned) { |
|
584 CNW_LOGE("connect: GonkNativeWindow has been abandoned!"); |
|
585 return NO_INIT; |
|
586 } |
|
587 |
|
588 int err = NO_ERROR; |
|
589 switch (api) { |
|
590 case NATIVE_WINDOW_API_EGL: |
|
591 case NATIVE_WINDOW_API_CPU: |
|
592 case NATIVE_WINDOW_API_MEDIA: |
|
593 case NATIVE_WINDOW_API_CAMERA: |
|
594 if (mConnectedApi != NO_CONNECTED_API) { |
|
595 CNW_LOGE("connect: already connected (cur=%d, req=%d)", |
|
596 mConnectedApi, api); |
|
597 err = -EINVAL; |
|
598 } else { |
|
599 mConnectedApi = api; |
|
600 *outWidth = mDefaultWidth; |
|
601 *outHeight = mDefaultHeight; |
|
602 *outTransform = 0; |
|
603 } |
|
604 break; |
|
605 default: |
|
606 err = -EINVAL; |
|
607 break; |
|
608 } |
|
609 return err; |
|
610 } |
|
611 |
|
612 status_t GonkNativeWindow::disconnect(int api) { |
|
613 CNW_LOGD("disconnect: api=%d", api); |
|
614 |
|
615 int err = NO_ERROR; |
|
616 Mutex::Autolock lock(mMutex); |
|
617 |
|
618 if (mAbandoned) { |
|
619 // it is not really an error to disconnect after the surface |
|
620 // has been abandoned, it should just be a no-op. |
|
621 return NO_ERROR; |
|
622 } |
|
623 |
|
624 switch (api) { |
|
625 case NATIVE_WINDOW_API_EGL: |
|
626 case NATIVE_WINDOW_API_CPU: |
|
627 case NATIVE_WINDOW_API_MEDIA: |
|
628 case NATIVE_WINDOW_API_CAMERA: |
|
629 if (mConnectedApi == api) { |
|
630 mQueue.clear(); |
|
631 freeAllBuffersLocked(); |
|
632 mConnectedApi = NO_CONNECTED_API; |
|
633 mDequeueCondition.signal(); |
|
634 } else { |
|
635 CNW_LOGE("disconnect: connected to another api (cur=%d, req=%d)", |
|
636 mConnectedApi, api); |
|
637 err = -EINVAL; |
|
638 } |
|
639 break; |
|
640 default: |
|
641 CNW_LOGE("disconnect: unknown API %d", api); |
|
642 err = -EINVAL; |
|
643 break; |
|
644 } |
|
645 return err; |
|
646 } |
|
647 |
|
648 status_t GonkNativeWindow::setScalingMode(int mode) { |
|
649 return OK; |
|
650 } |
|
651 |
|
652 void GonkNativeWindow::setNewFrameCallback( |
|
653 GonkNativeWindowNewFrameCallback* aCallback) { |
|
654 CNW_LOGD("setNewFrameCallback"); |
|
655 Mutex::Autolock lock(mMutex); |
|
656 mNewFrameCallback = aCallback; |
|
657 } |
|
658 |
|
659 int GonkNativeWindow::query(int what, int* outValue) |
|
660 { |
|
661 Mutex::Autolock lock(mMutex); |
|
662 |
|
663 if (mAbandoned) { |
|
664 CNW_LOGE("query: GonkNativeWindow has been abandoned!"); |
|
665 return NO_INIT; |
|
666 } |
|
667 |
|
668 int value; |
|
669 switch (what) { |
|
670 case NATIVE_WINDOW_WIDTH: |
|
671 value = mDefaultWidth; |
|
672 break; |
|
673 case NATIVE_WINDOW_HEIGHT: |
|
674 value = mDefaultHeight; |
|
675 break; |
|
676 case NATIVE_WINDOW_FORMAT: |
|
677 value = mPixelFormat; |
|
678 break; |
|
679 case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: |
|
680 value = MIN_UNDEQUEUED_BUFFERS; |
|
681 break; |
|
682 default: |
|
683 return BAD_VALUE; |
|
684 } |
|
685 outValue[0] = value; |
|
686 return NO_ERROR; |
|
687 } |