author | Kan-Ru Chen <kanru@kanru.info> |
Fri, 17 Aug 2012 18:56:55 +0800 | |
changeset 108638 | a4c49da8ed4e3f2c8df09f0ca7ad14a2cb7d0d0e |
parent 108637 | e1ebc55a599e4fda72aaab6c50de13c26a054d80 |
child 108639 | 22ce0a41fe217d074a2837ef9edbd04a70f2658d |
push id | 214 |
push user | akeybl@mozilla.com |
push date | Wed, 14 Nov 2012 20:38:59 +0000 |
treeherder | mozilla-release@c8b08ec8e1aa [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | cjones |
bugs | 757341 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -10,16 +10,17 @@ * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <string.h> +#include "base/basictypes.h" #include "libcameraservice/CameraHardwareInterface.h" #include "camera/CameraParameters.h" #include "nsCOMPtr.h" #include "nsDOMClassInfo.h" #include "nsMemory.h" #include "jsapi.h" #include "nsThread.h" #include "nsPrintfCString.h" @@ -562,24 +563,24 @@ nsGonkCameraControl::StartRecordingImpl( nsresult nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording) { return NS_ERROR_NOT_IMPLEMENTED; } void -nsGonkCameraControl::ReceiveFrame(PRUint8* aData, PRUint32 aLength) +nsGonkCameraControl::ReceiveFrame(layers::GraphicBufferLocked *aBuffer) { nsCOMPtr<CameraPreview> preview = mPreview; if (preview) { GonkCameraPreview* p = static_cast<GonkCameraPreview* >(preview.get()); MOZ_ASSERT(p); - p->ReceiveFrame(aData, aLength); + p->ReceiveFrame(aBuffer); } } // Gonk callback handlers. namespace mozilla { void ReceiveImage(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength) @@ -589,14 +590,14 @@ ReceiveImage(nsGonkCameraControl* gc, PR void AutoFocusComplete(nsGonkCameraControl* gc, bool success) { gc->AutoFocusComplete(success); } void -ReceiveFrame(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength) +ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked *aBuffer) { - gc->ReceiveFrame(aData, aLength); + gc->ReceiveFrame(aBuffer); } } // namespace mozilla
--- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -21,32 +21,36 @@ #include "prrwlock.h" #include "CameraControl.h" #define DOM_CAMERA_LOG_LEVEL 3 #include "CameraCommon.h" namespace mozilla { +namespace layers { +class GraphicBufferLocked; +} + class nsGonkCameraControl : public nsCameraControl { public: nsGonkCameraControl(PRUint32 aCameraId, nsIThread* aCameraThread); const char* GetParameter(const char* aKey); const char* GetParameterConstChar(PRUint32 aKey); double GetParameterDouble(PRUint32 aKey); void GetParameter(PRUint32 aKey, nsTArray<dom::CameraRegion>& aRegions); void SetParameter(const char* aKey, const char* aValue); void SetParameter(PRUint32 aKey, const char* aValue); void SetParameter(PRUint32 aKey, double aValue); void SetParameter(PRUint32 aKey, const nsTArray<dom::CameraRegion>& aRegions); void PushParameters(); - void ReceiveFrame(PRUint8 *aData, PRUint32 aLength); + void ReceiveFrame(layers::GraphicBufferLocked* aBuffer); protected: ~nsGonkCameraControl(); nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream); nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus); nsresult TakePictureImpl(TakePictureTask* aTakePicture); nsresult StartRecordingImpl(StartRecordingTask* aStartRecording); @@ -64,13 +68,13 @@ protected: private: nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE; nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE; }; // camera driver callbacks void ReceiveImage(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength); void AutoFocusComplete(nsGonkCameraControl* gc, bool success); -void ReceiveFrame(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength); +void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer); } // namespace mozilla #endif // DOM_CAMERA_GONKCAMERACONTROL_H
--- a/dom/camera/GonkCameraHwMgr.cpp +++ b/dom/camera/GonkCameraHwMgr.cpp @@ -9,24 +9,27 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +#include "base/basictypes.h" #include "nsDebug.h" #include "GonkCameraHwMgr.h" #include "GonkNativeWindow.h" #define DOM_CAMERA_LOG_LEVEL 3 #include "CameraCommon.h" using namespace mozilla; +using namespace mozilla::layers; +using namespace android; #if GIHM_TIMING_RECEIVEFRAME #define INCLUDE_TIME_H 1 #endif #if GIHM_TIMING_OVERALL #define INCLUDE_TIME_H 1 #endif @@ -54,16 +57,27 @@ GonkCameraHardware::GonkCameraHardware(G , mNumFrames(0) , mTarget(aTarget) , mInitialized(false) { DOM_CAMERA_LOGI( "%s: this = %p (aTarget = %p)\n", __func__, (void* )this, (void* )aTarget ); init(); } +void +GonkCameraHardware::OnNewFrame() +{ + if (mClosing) { + return; + } + GonkNativeWindow* window = static_cast<GonkNativeWindow*>(mWindow.get()); + nsRefPtr<GraphicBufferLocked> buffer = window->getCurrentBuffer(); + ReceiveFrame(mTarget, buffer); +} + // Android data callback void GonkCameraHardware::DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser) { GonkCameraHardware* hw = GetHardware((PRUint32)aUser); if (!hw) { DOM_CAMERA_LOGW("%s:aUser = %d resolved to no camera hw\n", __func__, (PRUint32)aUser); return; @@ -71,17 +85,17 @@ GonkCameraHardware::DataCallback(int32_t if (hw->mClosing) { return; } GonkCamera* camera = hw->mTarget; if (camera) { switch (aMsgType) { case CAMERA_MSG_PREVIEW_FRAME: - ReceiveFrame(camera, (PRUint8*)aDataPtr->pointer(), aDataPtr->size()); + // Do nothing break; case CAMERA_MSG_COMPRESSED_IMAGE: ReceiveImage(camera, (PRUint8*)aDataPtr->pointer(), aDataPtr->size()); break; default: DOM_CAMERA_LOGE("Unhandled data callback event %d\n", aMsgType); @@ -144,17 +158,17 @@ GonkCameraHardware::init() char cameraDeviceName[4]; snprintf(cameraDeviceName, sizeof(cameraDeviceName), "%d", mCamera); mHardware = new CameraHardwareInterface(cameraDeviceName); if (mHardware->initialize(&mModule->common) != OK) { mHardware.clear(); return; } - mWindow = new android::GonkNativeWindow(); + mWindow = new GonkNativeWindow(this); if (sHwHandle == 0) { sHwHandle = 1; // don't use 0 } mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, NULL, (void*)sHwHandle); // initialize the local camera parameter database mParams = mHardware->getParameters(); @@ -183,16 +197,18 @@ GonkCameraHardware::ReleaseHandle(PRUint } DOM_CAMERA_LOGI("%s: before: sHwHandle = %d\n", __func__, sHwHandle); sHwHandle += 1; // invalidate old handles before deleting hw->mClosing = true; hw->mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS); hw->mHardware->stopPreview(); hw->mHardware->release(); + GonkNativeWindow* window = static_cast<GonkNativeWindow*>(hw->mWindow.get()); + window->abandon(); DOM_CAMERA_LOGI("%s: after: sHwHandle = %d\n", __func__, sHwHandle); delete hw; // destroy the camera hardware instance } PRUint32 GonkCameraHardware::GetHandle(GonkCamera* aTarget, PRUint32 aCamera) { ReleaseHandle(sHwHandle); @@ -363,18 +379,16 @@ GonkCameraHardware::PullParameters(PRUin } } int GonkCameraHardware::StartPreview() { const char* format; - mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); - DOM_CAMERA_LOGI("Preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS)); // try to set preferred image format and frame rate const char* const PREVIEW_FORMAT = "yuv420p"; const char* const BAD_PREVIEW_FORMAT = "yuv420sp"; mParams.setPreviewFormat(PREVIEW_FORMAT); mParams.setPreviewFrameRate(mFps); mHardware->setParameters(mParams);
--- a/dom/camera/GonkCameraHwMgr.h +++ b/dom/camera/GonkCameraHwMgr.h @@ -21,38 +21,42 @@ #include "binder/IMemory.h" #include "mozilla/ReentrantMonitor.h" #include "GonkCameraControl.h" #define DOM_CAMERA_LOG_LEVEL 3 #include "CameraCommon.h" +#include "GonkNativeWindow.h" + // config #define GIHM_TIMING_RECEIVEFRAME 0 #define GIHM_TIMING_OVERALL 1 using namespace mozilla; using namespace android; namespace mozilla { typedef class nsGonkCameraControl GonkCamera; -class GonkCameraHardware +class GonkCameraHardware : GonkNativeWindowNewFrameCallback { protected: GonkCameraHardware(GonkCamera* aTarget, PRUint32 aCamera); ~GonkCameraHardware(); void init(); static void DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser); static void NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, void* aUser); public: + virtual void OnNewFrame() MOZ_OVERRIDE; + static void ReleaseHandle(PRUint32 aHwHandle); static PRUint32 GetHandle(GonkCamera* aTarget, PRUint32 aCamera); static PRUint32 GetFps(PRUint32 aHwHandle); static void GetPreviewSize(PRUint32 aHwHandle, PRUint32* aWidth, PRUint32* aHeight); static void SetPreviewSize(PRUint32 aHwHandle, PRUint32 aWidth, PRUint32 aHeight); static int AutoFocus(PRUint32 aHwHandle); static void CancelAutoFocus(PRUint32 aHwHandle); static int TakePicture(PRUint32 aHwHandle);
--- a/dom/camera/GonkCameraPreview.cpp +++ b/dom/camera/GonkCameraPreview.cpp @@ -9,174 +9,62 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +#include "base/basictypes.h" #include "VideoUtils.h" #include "GonkCameraHwMgr.h" #include "GonkCameraPreview.h" #define DOM_CAMERA_LOG_LEVEL 2 #include "CameraCommon.h" +#include "GonkIOSurfaceImage.h" + using namespace mozilla; -/** - * This big macro takes two 32-bit input blocks of interlaced u and - * v data (from a yuv420sp frame) in 's0' and 's1', and deinterlaces - * them into pairs of contiguous 32-bit blocks, the u plane data in - * 'u', and the v plane data in 'v' (i.e. for a yuv420p frame). - * - * yuv420sp: - * [ y-data ][ uv-data ] - * [ uv-data ]: [u0][v0][u1][v1][u2][v2]... - * - * yuv420p: - * [ y-data ][ u-data ][ v-data ] - * [ u-data ]: [u0][u1][u2]... - * [ v-data ]: [v0][v1][v2]... - * - * Doing this in 32-bit blocks is significantly faster than using - * byte-wise operations on ARM. (In some cases, the byte-wise - * de-interlacing can be too slow to keep up with the preview frames - * coming from the driver. - */ -#define DEINTERLACE( u, v, s0, s1 ) \ - u = ( (s0) & 0xFF00UL ) >> 8 | ( (s0) & 0xFF000000UL ) >> 16; \ - u |= ( (s1) & 0xFF00UL ) << 8 | ( (s1) & 0xFF000000UL ); \ - v = ( (s0) & 0xFFUL ) | ( (s0) & 0xFF0000UL ) >> 8; \ - v |= ( (s1) & 0xFFUL ) << 16 | ( (s1) & 0xFF0000UL ) << 8; - void -GonkCameraPreview::ReceiveFrame(PRUint8 *aData, PRUint32 aLength) +GonkCameraPreview::ReceiveFrame(mozilla::layers::GraphicBufferLocked* aBuffer) { DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this); - - if (mInput->HaveEnoughBuffered(TRACK_VIDEO)) { - if (mDiscardedFrameCount == 0) { - DOM_CAMERA_LOGI("mInput has enough data buffered, starting to discard\n"); - } - ++mDiscardedFrameCount; + if (!aBuffer) return; - } else if (mDiscardedFrameCount) { - DOM_CAMERA_LOGI("mInput needs more data again; discarded %d frames in a row\n", mDiscardedFrameCount); - mDiscardedFrameCount = 0; - } - - switch (mFormat) { - case GonkCameraHardware::PREVIEW_FORMAT_YUV420SP: - { - // de-interlace the u and v planes - uint8_t* y = aData; - uint32_t yN = mWidth * mHeight; - - NS_ASSERTION((yN & 0x3) == 0, "Invalid image dimensions!"); - - uint32_t uvN = yN / 4; - uint32_t* src = (uint32_t*)( y + yN ); - uint32_t* d = new uint32_t[ uvN / 2 ]; - uint32_t* u = d; - uint32_t* v = u + uvN / 4; - - // we're handling pairs of 32-bit words, so divide by 8 - NS_ASSERTION((uvN & 0x7) == 0, "Invalid image dimensions!"); - uvN /= 8; - - while (uvN--) { - uint32_t src0 = *src++; - uint32_t src1 = *src++; - - uint32_t u0; - uint32_t v0; - uint32_t u1; - uint32_t v1; - - DEINTERLACE( u0, v0, src0, src1 ); - - src0 = *src++; - src1 = *src++; - - DEINTERLACE( u1, v1, src0, src1 ); - *u++ = u0; - *u++ = u1; - *v++ = v0; - *v++ = v1; - } - - memcpy(y + yN, d, yN / 2); - delete[] d; - } - break; - - case GonkCameraHardware::PREVIEW_FORMAT_YUV420P: - // no transformating required - break; - - default: - // in a format we don't handle, get out of here - return; - } - - Image::Format format = Image::PLANAR_YCBCR; + Image::Format format = Image::GONK_IO_SURFACE; nsRefPtr<Image> image = mImageContainer->CreateImage(&format, 1); - image->AddRef(); - PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(image.get()); + GonkIOSurfaceImage* videoImage = static_cast<GonkIOSurfaceImage*>(image.get()); + GonkIOSurfaceImage::Data data; + data.mGraphicBuffer = aBuffer; + data.mPicSize = gfxIntSize(mWidth, mHeight); + videoImage->SetData(data); - /** - * If you change either lumaBpp or chromaBpp, make sure the - * assertions below still hold. - */ - const PRUint8 lumaBpp = 8; - const PRUint8 chromaBpp = 4; - PlanarYCbCrImage::Data data; - data.mYChannel = aData; - data.mYSize = gfxIntSize(mWidth, mHeight); - - data.mYStride = mWidth * lumaBpp; - NS_ASSERTION((data.mYStride & 0x7) == 0, "Invalid image dimensions!"); - data.mYStride /= 8; - - data.mCbCrStride = mWidth * chromaBpp; - NS_ASSERTION((data.mCbCrStride & 0x7) == 0, "Invalid image dimensions!"); - data.mCbCrStride /= 8; - - data.mCbChannel = aData + mHeight * data.mYStride; - data.mCrChannel = data.mCbChannel + mHeight * data.mCbCrStride / 2; - data.mCbCrSize = gfxIntSize(mWidth / 2, mHeight / 2); - data.mPicX = 0; - data.mPicY = 0; - data.mPicSize = gfxIntSize(mWidth, mHeight); - data.mStereoMode = mozilla::layers::STEREO_MODE_MONO; - videoImage->SetData(data); // copies buffer - - mVideoSegment.AppendFrame(videoImage, 1, gfxIntSize(mWidth, mHeight)); + // AppendFrame() takes over image's reference + mVideoSegment.AppendFrame(image.forget(), 1, gfxIntSize(mWidth, mHeight)); mInput->AppendToTrack(TRACK_VIDEO, &mVideoSegment); mFrameCount += 1; if ((mFrameCount % 10) == 0) { DOM_CAMERA_LOGI("%s:%d : mFrameCount = %d\n", __func__, __LINE__, mFrameCount); } } nsresult GonkCameraPreview::StartImpl() { DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this); /** * We set and then immediately get the preview size, in case the camera - * driver has decided to ignore our given dimensions. We need to know - * the dimensions the driver is using so that, if needed, we can properly - * de-interlace the yuv420sp format in ReceiveFrame() above. + * driver has decided to ignore our given dimensions. */ GonkCameraHardware::SetPreviewSize(mHwHandle, mWidth, mHeight); GonkCameraHardware::GetPreviewSize(mHwHandle, &mWidth, &mHeight); SetFrameRate(GonkCameraHardware::GetFps(mHwHandle)); if (GonkCameraHardware::StartPreview(mHwHandle) != OK) { DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__); return NS_ERROR_FAILURE;
--- a/dom/camera/GonkCameraPreview.h +++ b/dom/camera/GonkCameraPreview.h @@ -18,28 +18,34 @@ #define DOM_CAMERA_GONKCAMERAPREVIEW_H #include "CameraPreview.h" #define DOM_CAMERA_LOG_LEVEL 3 #include "CameraCommon.h" namespace mozilla { +namespace layers { +class GraphicBufferLocked; +} // namespace layers +} // namespace mozilla + +namespace mozilla { class GonkCameraPreview : public CameraPreview { public: GonkCameraPreview(nsIThread* aCameraThread, PRUint32 aHwHandle, PRUint32 aWidth, PRUint32 aHeight) : CameraPreview(aCameraThread, aWidth, aHeight) , mHwHandle(aHwHandle) , mDiscardedFrameCount(0) , mFormat(GonkCameraHardware::PREVIEW_FORMAT_UNKNOWN) { } - void ReceiveFrame(PRUint8 *aData, PRUint32 aLength); + void ReceiveFrame(layers::GraphicBufferLocked* aBuffer); nsresult StartImpl(); nsresult StopImpl(); protected: ~GonkCameraPreview() { Stop();
--- a/dom/camera/GonkNativeWindow.cpp +++ b/dom/camera/GonkNativeWindow.cpp @@ -1,8 +1,10 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 et sw=4 tw=80: */ /* * Copyright (C) 2010 The Android Open Source Project * Copyright (C) 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -10,41 +12,60 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +#include "base/basictypes.h" +#include "GonkCameraHwMgr.h" +#include "mozilla/layers/ShadowLayerUtilsGralloc.h" +#include "mozilla/layers/ImageBridgeChild.h" #include "GonkNativeWindow.h" #include "nsDebug.h" // enable debug logging by setting to 1 #define CNW_DEBUG 0 #if CNW_DEBUG #define CNW_LOGD(...) {(void)printf_stderr(__VA_ARGS__);} #else #define CNW_LOGD(...) ((void)0) #endif #define CNW_LOGE(...) {(void)printf_stderr(__VA_ARGS__);} using namespace android; +using namespace mozilla::layers; + +GonkNativeWindow::GonkNativeWindow(GonkNativeWindowNewFrameCallback* aCallback) + : mNewFrameCallback(aCallback) +{ + GonkNativeWindow::init(); +} GonkNativeWindow::GonkNativeWindow() + : mNewFrameCallback(nullptr) { GonkNativeWindow::init(); } GonkNativeWindow::~GonkNativeWindow() { freeAllBuffersLocked(); } +void GonkNativeWindow::abandon() +{ + Mutex::Autolock lock(mMutex); + freeAllBuffersLocked(); + mDequeueCondition.signal(); +} + void GonkNativeWindow::init() { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; ANativeWindow::dequeueBuffer = hook_dequeueBuffer; ANativeWindow::cancelBuffer = hook_cancelBuffer; ANativeWindow::lockBuffer = hook_lockBuffer; ANativeWindow::queueBuffer = hook_queueBuffer; @@ -107,21 +128,27 @@ int GonkNativeWindow::hook_perform(ANati va_list args; va_start(args, operation); GonkNativeWindow* c = getSelf(window); return c->perform(operation, args); } void GonkNativeWindow::freeBufferLocked(int i) { + ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton(); if (mSlots[i].mGraphicBuffer != NULL) { - mSlots[i].mGraphicBuffer.clear(); + // Don't destroy the gralloc buffer if it is still in the + // video stream awaiting rendering. + if (mSlots[i].mBufferState != BufferSlot::RENDERING) { + ibc->DeallocSurfaceDescriptorGralloc(mSlots[i].mSurfaceDescriptor); + } mSlots[i].mGraphicBuffer = NULL; + mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; } - mSlots[i].mBufferState = BufferSlot::FREE; } void GonkNativeWindow::freeAllBuffersLocked() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } } @@ -141,19 +168,21 @@ int GonkNativeWindow::setBufferCount(int } if (bufferCount < MIN_BUFFER_SLOTS) { CNW_LOGE("setBufferCount: requested buffer count (%d) is less than " "minimum (%d)", bufferCount, MIN_BUFFER_SLOTS); return BAD_VALUE; } - // Error out if the user has dequeued buffers + // Error out if the user has dequeued buffers or sent buffers to + // video stream for (int i=0 ; i<mBufferCount ; i++) { - if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { + if (mSlots[i].mBufferState == BufferSlot::DEQUEUED || + mSlots[i].mBufferState == BufferSlot::RENDERING) { CNW_LOGE("setBufferCount: client owns some buffers"); return -EINVAL; } } if (bufferCount > mBufferCount) { // easy, we just have more buffers mBufferCount = bufferCount; @@ -200,17 +229,19 @@ int GonkNativeWindow::dequeueBuffer(andr } } } // we're in synchronous mode and didn't find a buffer, we need to // wait for some buffers to be consumed tryAgain = (found == INVALID_BUFFER_SLOT); if (tryAgain) { + CNW_LOGD("dequeueBuffer: Try again"); mDequeueCondition.wait(mMutex); + CNW_LOGD("dequeueBuffer: Now"); } } if (found == INVALID_BUFFER_SLOT) { // This should not happen. CNW_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } @@ -219,23 +250,35 @@ int GonkNativeWindow::dequeueBuffer(andr // buffer is now in DEQUEUED mSlots[buf].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer); if (gbuf == NULL) { status_t error; - sp<GraphicBuffer> graphicBuffer( new GraphicBuffer( mDefaultWidth, mDefaultHeight, mPixelFormat, mUsage)); + SurfaceDescriptor buffer; + ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton(); + ibc->AllocSurfaceDescriptorGralloc(gfxIntSize(mDefaultWidth, mDefaultHeight), + mPixelFormat, + mUsage, + &buffer); + sp<GraphicBuffer> graphicBuffer = + GrallocBufferActor::GetFrom(buffer.get_SurfaceDescriptorGralloc()); + if (!graphicBuffer.get()) { + return -ENOMEM; + } error = graphicBuffer->initCheck(); if (error != NO_ERROR) { CNW_LOGE("dequeueBuffer: createGraphicBuffer failed with error %d",error); return error; } mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mSurfaceDescriptor = buffer; + mSlots[buf].mSurfaceDescriptor.get_SurfaceDescriptorGralloc().external() = true; } *buffer = mSlots[buf].mGraphicBuffer.get(); CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, mSlots[buf].mGraphicBuffer->handle ); CNW_LOGD("dequeueBuffer: X"); return NO_ERROR; @@ -255,48 +298,105 @@ int GonkNativeWindow::getSlotFromBufferL } } CNW_LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); return BAD_VALUE; } int GonkNativeWindow::queueBuffer(ANativeWindowBuffer* buffer) { - Mutex::Autolock lock(mMutex); - CNW_LOGD("queueBuffer: E"); - int buf = getSlotFromBufferLocked(buffer); + { + Mutex::Autolock lock(mMutex); + CNW_LOGD("queueBuffer: E"); + int buf = getSlotFromBufferLocked(buffer); - if (buf < 0 || buf >= mBufferCount) { - CNW_LOGE("queueBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return -EINVAL; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - CNW_LOGE("queueBuffer: slot %d is not owned by the client " - "(state=%d)", buf, mSlots[buf].mBufferState); - return -EINVAL; + if (buf < 0 || buf >= mBufferCount) { + CNW_LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + CNW_LOGE("queueBuffer: slot %d is not owned by the client " + "(state=%d)", buf, mSlots[buf].mBufferState); + return -EINVAL; + } + + int64_t timestamp; + if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { + timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + } else { + timestamp = mTimestamp; + } + + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mSlots[buf].mTimestamp = timestamp; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + mDequeueCondition.signal(); } - int64_t timestamp; - if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { - timestamp = systemTime(SYSTEM_TIME_MONOTONIC); - } else { - timestamp = mTimestamp; + // OnNewFrame might call lockCurrentBuffer so we must release the + // mutex first. + if (mNewFrameCallback) { + mNewFrameCallback->OnNewFrame(); } + CNW_LOGD("queueBuffer: X"); + return OK; +} + + +already_AddRefed<GraphicBufferLocked> +GonkNativeWindow::getCurrentBuffer() +{ + CNW_LOGD("GonkNativeWindow::lockCurrentBuffer"); + Mutex::Autolock lock(mMutex); + + int found = -1; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::QUEUED) { + if (found < 0 || + mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + found = i; + } + } + } - // Set the state to FREE as there are no operations on the queued buffer - // And, so that the buffer can be dequeued when needed. - mSlots[buf].mBufferState = BufferSlot::FREE; - mSlots[buf].mTimestamp = timestamp; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; + if (found < 0) { + mDequeueCondition.signal(); + return NULL; + } + + mSlots[found].mBufferState = BufferSlot::RENDERING; + + nsRefPtr<GraphicBufferLocked> ret = + new CameraGraphicBuffer(this, found, mSlots[found].mSurfaceDescriptor); + mDequeueCondition.signal(); + return ret.forget(); +} - mDequeueCondition.signal(); - CNW_LOGD("queueBuffer: X"); +void +GonkNativeWindow::returnBuffer(uint32_t aIndex) +{ + CNW_LOGD("GonkNativeWindow::freeBuffer"); + Mutex::Autolock lock(mMutex); - return OK; + if (aIndex < 0 || aIndex >= mBufferCount) { + CNW_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + mBufferCount, aIndex); + return; + } else if (mSlots[aIndex].mBufferState != BufferSlot::RENDERING) { + printf_stderr("cancelBuffer: slot %d is not owned by the compositor (state=%d)", + aIndex, mSlots[aIndex].mBufferState); + return; + } + + mSlots[aIndex].mBufferState = BufferSlot::FREE; + mDequeueCondition.signal(); + return; } int GonkNativeWindow::lockBuffer(ANativeWindowBuffer* buffer) { CNW_LOGD("GonkNativeWindow::lockBuffer"); Mutex::Autolock lock(mMutex); return OK; }
--- a/dom/camera/GonkNativeWindow.h +++ b/dom/camera/GonkNativeWindow.h @@ -1,8 +1,10 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 et sw=4 tw=80: */ /* * Copyright (C) 2010 The Android Open Source Project * Copyright (C) 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -26,37 +28,61 @@ #include <utils/Errors.h> #include <utils/RefBase.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> #include <utils/String8.h> #include <utils/threads.h> +#include "mozilla/layers/LayersSurfaces.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "GonkIOSurfaceImage.h" + namespace android { +// The user of GonkNativeWindow who wants to receive notification of +// new frames should implement this interface. +class GonkNativeWindowNewFrameCallback { +public: + virtual void OnNewFrame() = 0; +}; + class GonkNativeWindow : public EGLNativeBase<ANativeWindow, GonkNativeWindow, RefBase> { + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; public: enum { MIN_UNDEQUEUED_BUFFERS = 2 }; enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS }; enum { NUM_BUFFER_SLOTS = 32 }; GonkNativeWindow(); + GonkNativeWindow(GonkNativeWindowNewFrameCallback* aCallback); ~GonkNativeWindow(); // this class cannot be overloaded // ANativeWindow hooks static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer); static int hook_lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); static int hook_perform(ANativeWindow* window, int operation, ...); static int hook_query(const ANativeWindow* window, int what, int* value); static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); static int hook_setSwapInterval(ANativeWindow* window, int interval); + // Get next frame from the queue and mark it as RENDERING, caller + // owns the returned buffer. + already_AddRefed<GraphicBufferLocked> getCurrentBuffer(); + + // Return the buffer to the queue and mark it as FREE. After that + // the buffer is useable again for the decoder. + void returnBuffer(uint32_t index); + + // Release all internal buffers + void abandon(); + protected: virtual int cancelBuffer(ANativeWindowBuffer* buffer); virtual int dequeueBuffer(ANativeWindowBuffer** buffer); virtual int lockBuffer(ANativeWindowBuffer* buffer); virtual int perform(int operation, va_list args); virtual int query(int what, int* value) const; virtual int queueBuffer(ANativeWindowBuffer* buffer); virtual int setSwapInterval(int interval); @@ -98,16 +124,19 @@ private: mTimestamp(0), mFrameNumber(0){ } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. sp<GraphicBuffer> mGraphicBuffer; + // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. + SurfaceDescriptor mSurfaceDescriptor; + // BufferState represents the different states in which a buffer slot // can be. enum BufferState { // FREE indicates that the buffer is not currently being used and // will not be used in the future until it gets dequeued and // subsequently queued by the client. FREE = 0, @@ -126,16 +155,22 @@ private: // QUEUED indicates that the buffer has been queued by the client, // and has not since been made available for the client to dequeue. // Attaching the buffer to the texture does NOT transition the // buffer away from the QUEUED state. However, in Synchronous mode // the current buffer may be dequeued by the client under some // circumstances. See the note about the current buffer in the // documentation for DEQUEUED. QUEUED = 2, + + // RENDERING indicates that the buffer has been sent to + // the compositor, and has not yet available for the + // client to dequeue. When the compositor has finished its + // job, the buffer will be returned to FREE state. + RENDERING = 3, }; // mBufferState is the current state of this buffer slot. BufferState mBufferState; // mTimestamp is the current timestamp for this buffer slot. This gets // to set by queueBuffer each time this slot is queued. int64_t mTimestamp; @@ -180,13 +215,57 @@ private: int mBufferCount; // mMutex is the mutex used to prevent concurrent access to the member // variables. It must be locked whenever the member variables are accessed. mutable Mutex mMutex; // mFrameCounter is the free running counter, incremented for every buffer queued uint64_t mFrameCounter; + + GonkNativeWindowNewFrameCallback* mNewFrameCallback; +}; + + +// CameraGraphicBuffer maintains the buffer returned from GonkNativeWindow +class CameraGraphicBuffer : public mozilla::layers::GraphicBufferLocked { + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; +public: + CameraGraphicBuffer(GonkNativeWindow* aNativeWindow, + uint32_t aIndex, + SurfaceDescriptor aBuffer) + : GraphicBufferLocked(aBuffer) + , mNativeWindow(aNativeWindow) + , mIndex(aIndex) + , mLocked(true) + {} + + virtual ~CameraGraphicBuffer() {} + + // Unlock either returns the buffer to the native window or + // destroys the buffer if the window is already released. + virtual void Unlock() MOZ_OVERRIDE + { + if (mLocked) { + // The window might has been destroyed. The buffer is no longer + // valid at that point. + sp<GonkNativeWindow> window = mNativeWindow.promote(); + if (window.get()) { + window->returnBuffer(mIndex); + mLocked = false; + } else { + // If the window doesn't exist any more, release the buffer by + // ourself. + ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton(); + ibc->DeallocSurfaceDescriptorGralloc(mSurfaceDescriptor); + } + } + } + +protected: + wp<GonkNativeWindow> mNativeWindow; + uint32_t mIndex; + bool mLocked; }; }; // namespace android #endif // DOM_CAMERA_GONKNATIVEWINDOW_H
--- a/dom/camera/Makefile.in +++ b/dom/camera/Makefile.in @@ -45,8 +45,9 @@ XPIDLSRCS = \ nsIDOMCameraManager.idl \ $(NULL) EXPORTS = \ DOMCameraManager.h \ $(NULL) include $(topsrcdir)/config/rules.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/gfx/layers/GonkIOSurfaceImage.h +++ b/gfx/layers/GonkIOSurfaceImage.h @@ -3,16 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GONKIOSURFACEIMAGE_H #define GONKIOSURFACEIMAGE_H #ifdef MOZ_WIDGET_GONK +#include "mozilla/layers/LayersSurfaces.h" #include "ImageLayers.h" #include <ui/GraphicBuffer.h> namespace mozilla { namespace layers { /** @@ -23,40 +24,39 @@ namespace layers { * buffer content is guaranteed to not change until Unlock() is * called. Each producer must maintain their own buffer queue and * implement the GraphicBufferLocked::Unlock() interface. */ class GraphicBufferLocked { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphicBufferLocked) public: - GraphicBufferLocked(android::GraphicBuffer* aGraphicBuffer) - : mGraphicBuffer(aGraphicBuffer) + GraphicBufferLocked(SurfaceDescriptor aGraphicBuffer) + : mSurfaceDescriptor(aGraphicBuffer) {} virtual ~GraphicBufferLocked() {} virtual void Unlock() {} - virtual void* GetNativeBuffer() + SurfaceDescriptor GetSurfaceDescriptor() { - return mGraphicBuffer->getNativeBuffer(); + return mSurfaceDescriptor; } protected: - android::GraphicBuffer* mGraphicBuffer; + SurfaceDescriptor mSurfaceDescriptor; }; class THEBES_API GonkIOSurfaceImage : public Image { public: struct Data { nsRefPtr<GraphicBufferLocked> mGraphicBuffer; gfxIntSize mPicSize; }; - GonkIOSurfaceImage() : Image(NULL, GONK_IO_SURFACE) , mSize(0, 0) {} virtual ~GonkIOSurfaceImage() { mGraphicBuffer->Unlock(); @@ -76,17 +76,22 @@ public: virtual already_AddRefed<gfxASurface> GetAsSurface() { // We need to fix this and return a ASurface at some point. return nullptr; } void* GetNativeBuffer() { - return mGraphicBuffer->GetNativeBuffer(); + return GrallocBufferActor::GetFrom(GetSurfaceDescriptor())->getNativeBuffer(); + } + + SurfaceDescriptor GetSurfaceDescriptor() + { + return mGraphicBuffer->GetSurfaceDescriptor(); } private: nsRefPtr<GraphicBufferLocked> mGraphicBuffer; gfxIntSize mSize; }; } // namespace layers
--- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -5,16 +5,17 @@ #include "ImageBridgeChild.h" #include "ImageContainerChild.h" #include "CompositorParent.h" #include "ImageBridgeParent.h" #include "gfxSharedImageSurface.h" #include "ImageLayers.h" #include "base/thread.h" +#include "mozilla/Monitor.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/layers/ShadowLayers.h" using namespace base; namespace mozilla { namespace layers { @@ -65,16 +66,60 @@ static void CreateContainerChildSync(nsR bool *aDone) { ReentrantMonitorAutoEnter autoMon(*barrier); *result = sImageBridgeChildSingleton->CreateImageContainerChildNow(); *aDone = true; barrier->NotifyAll(); } +struct GrallocParam { + gfxIntSize size; + uint32_t format; + uint32_t usage; + SurfaceDescriptor* buffer; + + GrallocParam(const gfxIntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + SurfaceDescriptor* aBuffer) + : size(aSize) + , format(aFormat) + , usage(aUsage) + , buffer(aBuffer) + {} +}; + +// dispatched function +static void AllocSurfaceDescriptorGrallocSync(const GrallocParam& aParam, + Monitor* aBarrier, + bool* aDone) +{ + MonitorAutoLock autoMon(*aBarrier); + + sImageBridgeChildSingleton->AllocSurfaceDescriptorGrallocNow(aParam.size, + aParam.format, + aParam.usage, + aParam.buffer); + *aDone = true; + aBarrier->NotifyAll(); +} + +// dispatched function +static void DeallocSurfaceDescriptorGrallocSync(const SurfaceDescriptor& aBuffer, + Monitor* aBarrier, + bool* aDone) +{ + MonitorAutoLock autoMon(*aBarrier); + + sImageBridgeChildSingleton->DeallocSurfaceDescriptorGrallocNow(aBuffer); + *aDone = true; + aBarrier->NotifyAll(); +} + // dispatched function static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent) { MessageLoop *parentMsgLoop = parent->GetMessageLoop(); ipc::AsyncChannel *parentChannel = parent->GetIPCChannel(); child->Open(parentChannel, parentMsgLoop, mozilla::ipc::AsyncChannel::Child); } @@ -216,10 +261,120 @@ already_AddRefed<ImageContainerChild> Im { nsRefPtr<ImageContainerChild> ctnChild = new ImageContainerChild(); PRUint64 id = 0; SendPImageContainerConstructor(ctnChild, &id); ctnChild->SetID(id); return ctnChild.forget(); } +PGrallocBufferChild* +ImageBridgeChild::AllocPGrallocBuffer(const gfxIntSize&, const uint32_t&, const uint32_t&, + MaybeMagicGrallocBufferHandle*) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + return GrallocBufferActor::Create(); +#else + NS_RUNTIMEABORT("No gralloc buffers for you"); + return nullptr; +#endif +} + +bool +ImageBridgeChild::DeallocPGrallocBuffer(PGrallocBufferChild* actor) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + delete actor; + return true; +#else + NS_RUNTIMEABORT("Um, how did we get here?"); + return false; +#endif +} + +bool +ImageBridgeChild::AllocSurfaceDescriptorGralloc(const gfxIntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + SurfaceDescriptor* aBuffer) +{ + if (InImageBridgeChildThread()) { + return ImageBridgeChild::AllocSurfaceDescriptorGrallocNow(aSize, aFormat, aUsage, aBuffer); + } + + Monitor barrier("AllocSurfaceDescriptorGralloc Lock"); + MonitorAutoLock autoMon(barrier); + bool done = false; + + GetMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(&AllocSurfaceDescriptorGrallocSync, + GrallocParam(aSize, aFormat, aUsage, aBuffer), &barrier, &done)); + + while (!done) { + barrier.Wait(); + } + return true; +} + +bool +ImageBridgeChild::AllocSurfaceDescriptorGrallocNow(const gfxIntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + SurfaceDescriptor* aBuffer) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + MaybeMagicGrallocBufferHandle handle; + PGrallocBufferChild* gc = SendPGrallocBufferConstructor(aSize, aFormat, aUsage, &handle); + if (handle.Tnull_t == handle.type()) { + PGrallocBufferChild::Send__delete__(gc); + return false; + } + + GrallocBufferActor* gba = static_cast<GrallocBufferActor*>(gc); + gba->InitFromHandle(handle.get_MagicGrallocBufferHandle()); + + *aBuffer = SurfaceDescriptorGralloc(nullptr, gc, /* external */ false); + return true; +#else + NS_RUNTIMEABORT("No gralloc buffers for you"); + return false; +#endif +} + +bool +ImageBridgeChild::DeallocSurfaceDescriptorGralloc(const SurfaceDescriptor& aBuffer) +{ + if (InImageBridgeChildThread()) { + return ImageBridgeChild::DeallocSurfaceDescriptorGrallocNow(aBuffer); + } + + Monitor barrier("DeallocSurfaceDescriptor Lock"); + MonitorAutoLock autoMon(barrier); + bool done = false; + + GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&DeallocSurfaceDescriptorGrallocSync, + aBuffer, &barrier, &done)); + + while (!done) { + barrier.Wait(); + } + + return true; +} + +bool +ImageBridgeChild::DeallocSurfaceDescriptorGrallocNow(const SurfaceDescriptor& aBuffer) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + PGrallocBufferChild* gbp = + aBuffer.get_SurfaceDescriptorGralloc().bufferChild(); + PGrallocBufferChild::Send__delete__(gbp); + + return true; +#else + NS_RUNTIMEABORT("Um, how did we get here?"); + return false; +#endif +} + } // layers } // mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.h +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -154,16 +154,62 @@ public: /** * Part of the creation of ImageCOntainerChild that is executed on the * ImageBridgeChild thread after invoking CreateImageContainerChild * * Must be called from the ImageBridgeChild thread. */ already_AddRefed<ImageContainerChild> CreateImageContainerChildNow(); + + virtual PGrallocBufferChild* + AllocPGrallocBuffer(const gfxIntSize&, const uint32_t&, const uint32_t&, + MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE; + + virtual bool + DeallocPGrallocBuffer(PGrallocBufferChild* actor) MOZ_OVERRIDE; + + /** + * Allocate a gralloc SurfaceDescriptor remotely. + */ + bool + AllocSurfaceDescriptorGralloc(const gfxIntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + SurfaceDescriptor* aBuffer); + + /** + * Part of the allocation of gralloc SurfaceDescriptor that is + * executed on the ImageBridgeChild thread after invoking + * AllocSurfaceDescriptorGralloc. + * + * Must be called from the ImageBridgeChild thread. + */ + bool + AllocSurfaceDescriptorGrallocNow(const gfxIntSize& aSize, + const uint32_t& aContent, + const uint32_t& aUsage, + SurfaceDescriptor* aBuffer); + + /** + * Deallocate a remotely allocated gralloc buffer. + */ + bool + DeallocSurfaceDescriptorGralloc(const SurfaceDescriptor& aBuffer); + + /** + * Part of the deallocation of gralloc SurfaceDescriptor that is + * executed on the ImageBridgeChild thread after invoking + * DeallocSurfaceDescriptorGralloc. + * + * Must be called from the ImageBridgeChild thread. + */ + bool + DeallocSurfaceDescriptorGrallocNow(const SurfaceDescriptor& aBuffer); + protected: ImageBridgeChild() {}; /** * Creates an ImageContainerChild and it's associated ImageContainerParent. * * The creation happens synchronously on the ImageBridgeChild thread, so if
--- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -38,16 +38,42 @@ bool ImageBridgeParent::RecvStop() static PRUint64 GenImageContainerID() { static PRUint64 sNextImageID = 1; ++sNextImageID; return sNextImageID; } +PGrallocBufferParent* +ImageBridgeParent::AllocPGrallocBuffer(const gfxIntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + MaybeMagicGrallocBufferHandle* aOutHandle) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + return GrallocBufferActor::Create(aSize, aFormat, aUsage, aOutHandle); +#else + NS_RUNTIMEABORT("No gralloc buffers for you"); + return nullptr; +#endif +} + +bool +ImageBridgeParent::DeallocPGrallocBuffer(PGrallocBufferParent* actor) +{ +#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC + delete actor; + return true; +#else + NS_RUNTIMEABORT("Um, how did we get here?"); + return false; +#endif +} + PImageContainerParent* ImageBridgeParent::AllocPImageContainer(PRUint64* aID) { PRUint64 id = GenImageContainerID(); *aID = id; return new ImageContainerParent(this, id); } bool ImageBridgeParent::DeallocPImageContainer(PImageContainerParent* toDealloc)
--- a/gfx/layers/ipc/ImageBridgeParent.h +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -18,16 +18,24 @@ class CompositorParent; */ class ImageBridgeParent : public PImageBridgeParent { public: ImageBridgeParent(MessageLoop* aLoop); ~ImageBridgeParent(); + + virtual PGrallocBufferParent* + AllocPGrallocBuffer(const gfxIntSize&, const uint32_t&, const uint32_t&, + MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE; + + virtual bool + DeallocPGrallocBuffer(PGrallocBufferParent* actor) MOZ_OVERRIDE; + // Overriden from PImageBridgeParent. PImageContainerParent* AllocPImageContainer(PRUint64* aID) MOZ_OVERRIDE; // Overriden from PImageBridgeParent. bool DeallocPImageContainer(PImageContainerParent* toDealloc) MOZ_OVERRIDE; // Overriden from PImageBridgeParent. bool RecvStop() MOZ_OVERRIDE; MessageLoop * GetMessageLoop();
--- a/gfx/layers/ipc/ImageContainerChild.cpp +++ b/gfx/layers/ipc/ImageContainerChild.cpp @@ -5,16 +5,17 @@ #include "ImageContainerChild.h" #include "gfxSharedImageSurface.h" #include "ShadowLayers.h" #include "mozilla/layers/PLayers.h" #include "mozilla/layers/SharedImageUtils.h" #include "ImageLayers.h" +#include "GonkIOSurfaceImage.h" namespace mozilla { namespace layers { /* * - POOL_MAX_SHARED_IMAGES is the maximum number number of shared images to * store in the ImageContainerChild's pool. * @@ -93,16 +94,20 @@ void ImageContainerChild::StopChild() mStop = true; DispatchDestroy(); } bool ImageContainerChild::RecvReturnImage(const SharedImage& aImage) { SharedImage* img = new SharedImage(aImage); + // Remove oldest image from the queue. + if (mImageQueue.Length() > 0) { + mImageQueue.RemoveElementAt(0); + } if (!AddSharedImageToPool(img) || mStop) { DestroySharedImage(*img); delete img; } return true; } void ImageContainerChild::DestroySharedImage(const SharedImage& aImage) @@ -191,16 +196,22 @@ SharedImage* ImageContainerChild::Create SharedImage* result = new SharedImage( YUVImage(tempBufferY->GetShmem(), tempBufferU->GetShmem(), tempBufferV->GetShmem(), data->GetPictureRect())); NS_ABORT_IF_FALSE(result->type() == SharedImage::TYUVImage, "SharedImage type not set correctly"); return result; +#ifdef MOZ_WIDGET_GONK + } else if (image->GetFormat() == Image::GONK_IO_SURFACE) { + GonkIOSurfaceImage* gonkImage = static_cast<GonkIOSurfaceImage*>(image); + SharedImage* result = new SharedImage(gonkImage->GetSurfaceDescriptor()); + return result; +#endif } else { NS_RUNTIMEABORT("TODO: Only YUVImage is supported here right now."); } return nullptr; } bool ImageContainerChild::AddSharedImageToPool(SharedImage* img) { @@ -314,16 +325,19 @@ SharedImage* ImageContainerChild::ImageT NS_ABORT_IF_FALSE(InImageBridgeChildThread(), "Should be in ImageBridgeChild thread."); SharedImage *img = GetSharedImageFor(aImage); if (img) { CopyDataIntoSharedImage(aImage, img); } else { img = CreateSharedImageFromData(aImage); } + // Keep a reference to the image we sent to compositor to maintain a + // correct reference count. + mImageQueue.AppendElement(aImage); return img; } void ImageContainerChild::SendImageAsync(ImageContainer* aContainer, Image* aImage) { if(!aContainer || !aImage) { return;
--- a/gfx/layers/ipc/ImageContainerChild.h +++ b/gfx/layers/ipc/ImageContainerChild.h @@ -187,16 +187,26 @@ protected: * Allocates a SharedImage and copy aImage's data into it. * Called by ImageToSharedImage. */ SharedImage * CreateSharedImageFromData(Image* aImage); private: PRUint64 mImageContainerID; nsTArray<SharedImage*> mSharedImagePool; + + /** + * Save a reference to the outgoing images and remove the reference + * once the image is returned from the compositor. + * GonkIOSurfaceImage needs to know when to return the buffer to the + * producing thread. The buffer is returned when GonkIOSurfaceImage + * destructs. + */ + nsTArray<nsRefPtr<Image> > mImageQueue; + int mActiveImageCount; bool mStop; bool mDispatchedDestroy; }; } // namespace } // namespace
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -40,16 +40,25 @@ struct SharedTextureDescriptor { TextureShareType shareType; SharedTextureHandle handle; nsIntSize size; bool inverted; }; struct SurfaceDescriptorGralloc { PGrallocBuffer buffer; + + /** + * We can have one source producing gralloc buffers and sharing them + * with another source that may also produce its own gralloc buffers. + * This happens for camera preview buffers sent to video code. When + * that happens, the producer can mark the buffer as "external" to + * prevent its consumer from mistakenly freeing the buffer. + */ + bool external; }; struct SharedImageID { PRUint64 id; }; union SurfaceDescriptor { Shmem;
--- a/gfx/layers/ipc/PGrallocBuffer.ipdl +++ b/gfx/layers/ipc/PGrallocBuffer.ipdl @@ -1,29 +1,31 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=8 et : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +include protocol PImageBridge; include protocol PImageContainer; include protocol PLayers; namespace mozilla { namespace layers { /** * This is a trivial protocol that's used to track gralloc buffers * across thread contexts. A live PGrallocBuffer actor always * corresponds 1:1 to a pre-shared gralloc buffer (sharing is done by * the PGrallocBuffer constructor). */ async protocol PGrallocBuffer { - manager PImageContainer or PLayers; + /* FIXME: Bug 783451: shouldn't be managed by PImageContainer */ + manager PImageBridge or PImageContainer or PLayers; /** Gralloc buffers can be "owned" by either parent or child. */ both: async __delete__(); }; } // namespace layers } // namespace mozilla
--- a/gfx/layers/ipc/PImageBridge.ipdl +++ b/gfx/layers/ipc/PImageBridge.ipdl @@ -1,30 +1,41 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +include LayersSurfaces; +include protocol PGrallocBuffer; include protocol PImageContainer; using ImageHandle; namespace mozilla { namespace layers { /** * The PImageBridge protocol is used to allow isolated threads or processes to push * frames directly to the compositor thread/process without relying on the main thread * which might be too busy dealing with content script. */ /*FIXME: sync*/rpc protocol PImageBridge { manages PImageContainer; + manages PGrallocBuffer; parent: + + // Allocates a gralloc buffer that may not suitable to use with + // gfxImageSurface but allows hardware decoder to write to the + // buffer directly. The format is a enum defined in + // system/graphics.h and the usage is the GraphicBuffer usage + // flag. See GraphicBuffer.h and gralloc.h. + sync PGrallocBuffer(gfxIntSize size, uint32_t format, uint32_t usage) + returns (MaybeMagicGrallocBufferHandle handle); // First step of the destruction sequence. This puts all the ImageContainerParents // in a state in which they can't send asynchronous messages to their child // counterpart so as to not race with the upcomming __delete__ message. // In the child side, the __delete__ messages are not sent right after Stop, // they are scheduled in the ImageBridgeChild's message queue in order to ensure // that all the messages from the parent side have been received and processed // before sending __delete__.
--- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp @@ -187,16 +187,34 @@ ShadowLayerManager::OpenDescriptorForDir } /*static*/ void ShadowLayerManager::PlatformSyncBeforeReplyUpdate() { // Nothing to be done for gralloc. } +/*static*/ PGrallocBufferParent* +GrallocBufferActor::Create(const gfxIntSize& aSize, + const uint32_t& aFormat, + const uint32_t& aUsage, + MaybeMagicGrallocBufferHandle* aOutHandle) +{ + GrallocBufferActor* actor = new GrallocBufferActor(); + *aOutHandle = null_t(); + sp<GraphicBuffer> buffer( + new GraphicBuffer(aSize.width, aSize.height, aFormat, aUsage)); + if (buffer->initCheck() != OK) + return actor; + + actor->mGraphicBuffer = buffer; + *aOutHandle = MagicGrallocBufferHandle(buffer); + return actor; +} + bool ShadowLayerManager::PlatformDestroySharedSurface(SurfaceDescriptor* aSurface) { if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aSurface->type()) { return false; } PGrallocBufferParent* gbp = @@ -239,17 +257,17 @@ ShadowLayerForwarder::PlatformAllocBuffe if (handle.Tnull_t == handle.type()) { PGrallocBufferChild::Send__delete__(gc); return false; } GrallocBufferActor* gba = static_cast<GrallocBufferActor*>(gc); gba->InitFromHandle(handle.get_MagicGrallocBufferHandle()); - *aBuffer = SurfaceDescriptorGralloc(nullptr, gc); + *aBuffer = SurfaceDescriptorGralloc(nullptr, gc, /* external */ false); return true; } //----------------------------------------------------------------------------- // Both processes /*static*/ sp<GraphicBuffer> GrallocBufferActor::GetFrom(const SurfaceDescriptorGralloc& aDescriptor)
--- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.h +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.h @@ -56,36 +56,41 @@ struct MagicGrallocBufferHandle { * GraphicBuffer (pmem region). It allows us to cheaply and * conveniently share gralloc handles between processes. */ class GrallocBufferActor : public PGrallocBufferChild , public PGrallocBufferParent { friend class ShadowLayerForwarder; friend class ShadowLayerManager; + friend class ImageBridgeChild; typedef android::GraphicBuffer GraphicBuffer; public: virtual ~GrallocBufferActor() {} static PGrallocBufferParent* Create(const gfxIntSize& aSize, const gfxContentType& aContent, MaybeMagicGrallocBufferHandle* aOutHandle); + static PGrallocBufferParent* + Create(const gfxIntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage, + MaybeMagicGrallocBufferHandle* aOutHandle); + static PGrallocBufferChild* Create(); + static android::sp<GraphicBuffer> + GetFrom(const SurfaceDescriptorGralloc& aDescriptor); + private: GrallocBufferActor() {} void InitFromHandle(const MagicGrallocBufferHandle& aHandle); - static android::sp<GraphicBuffer> - GetFrom(const SurfaceDescriptorGralloc& aDescriptor); - android::sp<GraphicBuffer> mGraphicBuffer; }; } // namespace layers } // namespace mozilla namespace IPC {
--- a/gfx/layers/ipc/SharedImageUtils.h +++ b/gfx/layers/ipc/SharedImageUtils.h @@ -15,17 +15,18 @@ namespace layers { template<typename Deallocator> void DeallocSharedImageData(Deallocator* protocol, const SharedImage& aImage) { if (aImage.type() == SharedImage::TYUVImage) { protocol->DeallocShmem(aImage.get_YUVImage().Ydata()); protocol->DeallocShmem(aImage.get_YUVImage().Udata()); protocol->DeallocShmem(aImage.get_YUVImage().Vdata()); - } else if (aImage.type() == SharedImage::TSurfaceDescriptor) { + } else if (aImage.type() == SharedImage::TSurfaceDescriptor && + aImage.get_SurfaceDescriptor().type() == SurfaceDescriptor::TShmem) { protocol->DeallocShmem(aImage.get_SurfaceDescriptor().get_Shmem()); } } template<typename Allocator> bool AllocateSharedBuffer(Allocator* protocol, const gfxIntSize& aSize, gfxASurface::gfxContentType aContent,
--- a/gfx/layers/opengl/ImageLayerOGL.cpp +++ b/gfx/layers/opengl/ImageLayerOGL.cpp @@ -890,16 +890,31 @@ ShadowImageLayerOGL::RenderLayer(int aPr mOGLManager->GetCompositorID()); PRUint32 imgVersion = ImageContainerParent::GetSharedImageVersion(mImageContainerID); if (imgVersion != mImageVersion) { SharedImage* img = ImageContainerParent::GetSharedImage(mImageContainerID); if (img && (img->type() == SharedImage::TYUVImage)) { UploadSharedYUVToTexture(img->get_YUVImage()); mImageVersion = imgVersion; +#ifdef MOZ_WIDGET_GONK + } else if (img + && (img->type() == SharedImage::TSurfaceDescriptor) + && (img->get_SurfaceDescriptor().type() == SurfaceDescriptor::TSurfaceDescriptorGralloc)) { + const SurfaceDescriptorGralloc& desc = img->get_SurfaceDescriptor().get_SurfaceDescriptorGralloc(); + sp<GraphicBuffer> graphicBuffer = GrallocBufferActor::GetFrom(desc); + mSize = gfxIntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight()); + if (!mExternalBufferTexture.IsAllocated()) { + mExternalBufferTexture.Allocate(gl()); + } + gl()->MakeCurrent(); + gl()->fActiveTexture(LOCAL_GL_TEXTURE0); + gl()->BindExternalBuffer(mExternalBufferTexture.GetTextureID(), graphicBuffer->getNativeBuffer()); + mImageVersion = imgVersion; +#endif } } } if (mTexImage) { NS_ASSERTION(mTexImage->GetContentType() != gfxASurface::CONTENT_ALPHA, "Image layer has alpha image"); @@ -930,16 +945,35 @@ ShadowImageLayerOGL::RenderLayer(int aPr // We can't use BindAndDrawQuad because that always uploads the whole texture from 0.0f -> 1.0f // in x and y. We use BindAndDrawQuadWithTextureRect to actually draw a subrect of the texture mOGLManager->BindAndDrawQuadWithTextureRect(colorProgram, nsIntRect(0, 0, mTexImage->GetTileRect().width, mTexImage->GetTileRect().height), mTexImage->GetTileRect().Size()); } while (mTexImage->NextTile()); } +#ifdef MOZ_WIDGET_GONK + } else if (mExternalBufferTexture.IsAllocated()) { + ShaderProgramOGL *program = mOGLManager->GetProgram(RGBAExternalLayerProgramType, GetMaskLayer()); + + gl()->ApplyFilterToBoundTexture(mFilter); + + program->Activate(); + program->SetLayerQuadRect(nsIntRect(0, 0, + mSize.width, mSize.height)); + program->SetLayerTransform(GetEffectiveTransform()); + program->SetLayerOpacity(GetEffectiveOpacity()); + program->SetRenderOffset(aOffset); + program->SetTextureUnit(0); + program->LoadMask(GetMaskLayer()); + + mOGLManager->BindAndDrawQuadWithTextureRect(program, + GetVisibleRegion().GetBounds(), + nsIntSize(mSize.width, mSize.height)); +#endif } else if (mSharedHandle) { GLContext::SharedHandleDetails handleDetails; if (!gl()->GetSharedHandleDetails(mShareType, mSharedHandle, handleDetails)) { NS_ERROR("Failed to get shared handle details"); return; } ShaderProgramOGL* program = mOGLManager->GetProgram(handleDetails.mProgramType, GetMaskLayer()); @@ -1019,16 +1053,17 @@ ShadowImageLayerOGL::LoadAsTexture(GLuin void ShadowImageLayerOGL::CleanupResources() { if (mSharedHandle) { gl()->ReleaseSharedHandle(mShareType, mSharedHandle); mSharedHandle = NULL; } + mExternalBufferTexture.Release(); mYUVTexture[0].Release(); mYUVTexture[1].Release(); mYUVTexture[2].Release(); mTexImage = nullptr; } } /* layers */ } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.h +++ b/gfx/layers/opengl/ImageLayerOGL.h @@ -181,17 +181,22 @@ private: nsRefPtr<TextureImage> mTexImage; // For SharedTextureHandle gl::SharedTextureHandle mSharedHandle; gl::TextureImage::TextureShareType mShareType; bool mInverted; GLuint mTexture; - + + // For direct texturing with OES_EGL_image_external extension. This + // texture is allocated when the image supports binding with + // BindExternalBuffer. + GLTexture mExternalBufferTexture; + GLTexture mYUVTexture[3]; gfxIntSize mSize; gfxIntSize mCbCrSize; nsIntRect mPictureRect; }; } /* layers */ } /* mozilla */