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