diff options
-rw-r--r-- | cmds/dumpstate/dumpstate.c | 1 | ||||
-rw-r--r-- | include/gui/BufferQueue.h | 5 | ||||
-rw-r--r-- | include/gui/CpuConsumer.h | 142 | ||||
-rw-r--r-- | include/utils/Trace.h | 3 | ||||
-rw-r--r-- | libs/gui/Android.mk | 3 | ||||
-rw-r--r-- | libs/gui/CpuConsumer.cpp | 231 | ||||
-rw-r--r-- | libs/gui/tests/Android.mk | 29 | ||||
-rw-r--r-- | libs/gui/tests/CpuConsumer_test.cpp | 579 | ||||
-rw-r--r-- | libs/utils/ZipFileRO.cpp | 4 | ||||
-rw-r--r-- | opengl/specs/EGL_ANDROID_fence_sync.txt | 213 | ||||
-rw-r--r-- | opengl/specs/README | 5 |
11 files changed, 1210 insertions, 5 deletions
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 165f11c..0d92fc0 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -88,6 +88,7 @@ static void dumpstate() { dump_file("KERNEL WAKELOCKS", "/proc/wakelocks"); + dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources"); dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); run_command("PROCESSES", 10, "ps", "-P", NULL); diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 1c80d0c..0539a1b 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -209,6 +209,11 @@ public: // releaseBuffer releases a buffer slot from the consumer back to the // BufferQueue pending a fence sync. // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // // Note that the dependencies on EGL will be removed once we switch to using // the Android HW Sync HAL. status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence); diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h new file mode 100644 index 0000000..a50a1de --- /dev/null +++ b/include/gui/CpuConsumer.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef ANDROID_GUI_CPUCONSUMER_H +#define ANDROID_GUI_CPUCONSUMER_H + +#include <gui/BufferQueue.h> + +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +#define ANDROID_GRAPHICS_CPUCONSUMER_JNI_ID "mCpuConsumer" + +namespace android { + +/** + * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU + * access to the underlying gralloc buffers provided by BufferQueue. Multiple + * buffers may be acquired by it at once, to be used concurrently by the + * CpuConsumer owner. Sets gralloc usage flags to be software-read-only. + * This queue is synchronous by default. + */ + +class CpuConsumer: public virtual RefBase, + protected BufferQueue::ConsumerListener +{ + public: + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called each time an additional frame becomes + // available for consumption. A new frame queued will always trigger the + // callback, whether the queue is empty or not. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + struct LockedBuffer { + uint8_t *data; + uint32_t width; + uint32_t height; + PixelFormat format; + uint32_t stride; + Rect crop; + uint32_t transform; + uint32_t scalingMode; + int64_t timestamp; + uint64_t frameNumber; + }; + + // Create a new CPU consumer. The maxLockedBuffers parameter specifies + // how many buffers can be locked for user access at the same time. + CpuConsumer(uint32_t maxLockedBuffers); + + virtual ~CpuConsumer(); + + // set the name of the CpuConsumer that will be used to identify it in + // log messages. + void setName(const String8& name); + + // Gets the next graphics buffer from the producer and locks it for CPU use, + // filling out the passed-in locked buffer structure with the native pointer + // and metadata. Returns BAD_VALUE if no new buffer is available, and + // INVALID_OPERATION if the maximum number of buffers is already locked. + // + // Only a fixed number of buffers can be locked at a time, determined by the + // construction-time maxLockedBuffers parameter. If INVALID_OPERATION is + // returned by lockNextBuffer, then old buffers must be returned to the queue + // by calling unlockBuffer before more buffers can be acquired. + status_t lockNextBuffer(LockedBuffer *nativeBuffer); + + // Returns a locked buffer to the queue, allowing it to be reused. Since + // only a fixed number of buffers may be locked at a time, old buffers must + // be released by calling unlockBuffer to ensure new buffers can be acquired by + // lockNextBuffer. + status_t unlockBuffer(const LockedBuffer &nativeBuffer); + + // setFrameAvailableListener sets the listener object that will be notified + // when a new frame becomes available. + void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); + + sp<ISurfaceTexture> getProducerInterface() const { return mBufferQueue; } + protected: + + // Implementation of the BufferQueue::ConsumerListener interface. These + // calls are used to notify the CpuConsumer of asynchronous events in the + // BufferQueue. + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + + private: + // Free local buffer state + status_t freeBufferLocked(int buf); + + // Maximum number of buffers that can be locked at a time + uint32_t mMaxLockedBuffers; + + // mName is a string used to identify the SurfaceTexture in log messages. + // It can be set by the setName method. + String8 mName; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + sp<FrameAvailableListener> mFrameAvailableListener; + + // Underlying buffer queue + sp<BufferQueue> mBufferQueue; + + // Array for caching buffers from the buffer queue + sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS]; + // Array for tracking pointers passed to the consumer, matching the + // mBufferSlot indexing + void *mBufferPointers[BufferQueue::NUM_BUFFER_SLOTS]; + // Count of currently locked buffers + uint32_t mCurrentLockedBuffers; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of CpuConsumer objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; +}; + +} // namespace android + +#endif // ANDROID_GUI_CPUCONSUMER_H diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 4219206..e5cc7ec 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -51,7 +51,8 @@ #define ATRACE_TAG_SYNC_MANAGER (1<<7) #define ATRACE_TAG_AUDIO (1<<8) #define ATRACE_TAG_VIDEO (1<<9) -#define ATRACE_TAG_LAST ATRACE_TAG_VIDEO +#define ATRACE_TAG_CAMERA (1<<10) +#define ATRACE_TAG_LAST ATRACE_TAG_CAMERA #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 8224847..8fc96cf 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -21,7 +21,8 @@ LOCAL_SRC_FILES:= \ LayerState.cpp \ Surface.cpp \ SurfaceComposerClient.cpp \ - DummyConsumer.cpp + DummyConsumer.cpp \ + CpuConsumer.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp new file mode 100644 index 0000000..48a54c7 --- /dev/null +++ b/libs/gui/CpuConsumer.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "CpuConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Log.h> + +#include <gui/CpuConsumer.h> + +#define CC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CC_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +CpuConsumer::CpuConsumer(uint32_t maxLockedBuffers) : + mMaxLockedBuffers(maxLockedBuffers), + mCurrentLockedBuffers(0) +{ + mName = String8::format("cc-unnamed-%d-%d", getpid(), + createProcessUniqueId()); + + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + mBufferPointers[i] = NULL; + } + + mBufferQueue = new BufferQueue(true); + + wp<BufferQueue::ConsumerListener> listener; + sp<BufferQueue::ConsumerListener> proxy; + listener = static_cast<BufferQueue::ConsumerListener*>(this); + proxy = new BufferQueue::ProxyConsumerListener(listener); + + status_t err = mBufferQueue->consumerConnect(proxy); + if (err != NO_ERROR) { + ALOGE("CpuConsumer: error connecting to BufferQueue: %s (%d)", + strerror(-err), err); + } else { + mBufferQueue->setSynchronousMode(true); + mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); + mBufferQueue->setConsumerName(mName); + } +} + +CpuConsumer::~CpuConsumer() +{ + Mutex::Autolock _l(mMutex); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } + mBufferQueue->consumerDisconnect(); + mBufferQueue.clear(); +} + +void CpuConsumer::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mBufferQueue->setConsumerName(name); +} + +status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { + status_t err; + + if (!nativeBuffer) return BAD_VALUE; + if (mCurrentLockedBuffers == mMaxLockedBuffers) { + return INVALID_OPERATION; + } + + BufferQueue::BufferItem b; + + Mutex::Autolock _l(mMutex); + + err = mBufferQueue->acquireBuffer(&b); + if (err != OK) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + return BAD_VALUE; + } else { + CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + return err; + } + } + + int buf = b.mBuf; + + if (b.mGraphicBuffer != NULL) { + if (mBufferPointers[buf] != NULL) { + CC_LOGE("Reallocation of buffer %d while in consumer use!", buf); + mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + return BAD_VALUE; + } + mBufferSlot[buf] = b.mGraphicBuffer; + } + + err = mBufferSlot[buf]->lock( + GraphicBuffer::USAGE_SW_READ_OFTEN, + b.mCrop, + &mBufferPointers[buf]); + + if (mBufferPointers[buf] != NULL && err != OK) { + CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), + err); + return err; + } + + nativeBuffer->data = reinterpret_cast<uint8_t*>(mBufferPointers[buf]); + nativeBuffer->width = mBufferSlot[buf]->getWidth(); + nativeBuffer->height = mBufferSlot[buf]->getHeight(); + nativeBuffer->format = mBufferSlot[buf]->getPixelFormat(); + nativeBuffer->stride = mBufferSlot[buf]->getStride(); + + nativeBuffer->crop = b.mCrop; + nativeBuffer->transform = b.mTransform; + nativeBuffer->scalingMode = b.mScalingMode; + nativeBuffer->timestamp = b.mTimestamp; + nativeBuffer->frameNumber = b.mFrameNumber; + + mCurrentLockedBuffers++; + + return OK; +} + +status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) { + Mutex::Autolock _l(mMutex); + int buf = 0; + status_t err; + + void *bufPtr = reinterpret_cast<void *>(nativeBuffer.data); + for (; buf < BufferQueue::NUM_BUFFER_SLOTS; buf++) { + if (bufPtr == mBufferPointers[buf]) break; + } + if (buf == BufferQueue::NUM_BUFFER_SLOTS) { + CC_LOGE("%s: Can't find buffer to free", __FUNCTION__); + return BAD_VALUE; + } + + mBufferPointers[buf] = NULL; + err = mBufferSlot[buf]->unlock(); + if (err != OK) { + CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, buf); + return err; + } + err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + if (err == BufferQueue::STALE_BUFFER_SLOT) { + freeBufferLocked(buf); + } else if (err != OK) { + CC_LOGE("%s: Unable to release graphic buffer %d to queue", __FUNCTION__, + buf); + return err; + } + + mCurrentLockedBuffers--; + + return OK; +} + +void CpuConsumer::setFrameAvailableListener( + const sp<FrameAvailableListener>& listener) { + CC_LOGV("setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + + +void CpuConsumer::onFrameAvailable() { + CC_LOGV("onFrameAvailable"); + sp<FrameAvailableListener> listener; + { // scope for the lock + Mutex::Autolock _l(mMutex); + listener = mFrameAvailableListener; + } + + if (listener != NULL) { + CC_LOGV("actually calling onFrameAvailable"); + listener->onFrameAvailable(); + } +} + +void CpuConsumer::onBuffersReleased() { + CC_LOGV("onBuffersReleased"); + + Mutex::Autolock lock(mMutex); + + uint32_t mask = 0; + mBufferQueue->getReleasedBuffers(&mask); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1 << i)) { + freeBufferLocked(i); + } + } + +} + +status_t CpuConsumer::freeBufferLocked(int buf) { + status_t err = OK; + + if (mBufferPointers[buf] != NULL) { + CC_LOGW("Buffer %d freed while locked by consumer", buf); + mBufferPointers[buf] = NULL; + err = mBufferSlot[buf]->unlock(); + if (err != OK) { + CC_LOGE("%s: Unable to unlock graphic buffer %d", __FUNCTION__, buf); + } + mCurrentLockedBuffers--; + } + mBufferSlot[buf] = NULL; + return err; +} + +} // namespace android diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk index 741534b..3a8e356 100644 --- a/libs/gui/tests/Android.mk +++ b/libs/gui/tests/Android.mk @@ -31,6 +31,35 @@ LOCAL_C_INCLUDES := \ # to integrate with auto-test framework. include $(BUILD_NATIVE_TEST) +include $(CLEAR_VARS) + +LOCAL_MODULE := CpuConsumer_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + CpuConsumer_test.cpp + +LOCAL_SHARED_LIBRARIES := \ + libEGL \ + libGLESv2 \ + libbinder \ + libcutils \ + libgui \ + libstlport \ + libui \ + libutils \ + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +# to integrate with auto-test framework. +include $(BUILD_NATIVE_TEST) + # Include subdirectory makefiles # ============================================================ diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp new file mode 100644 index 0000000..7ad60e8 --- /dev/null +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#define LOG_TAG "CpuConsumer_test" +//#define LOG_NDEBUG 0 +//#define LOG_NNDEBUG 0 + +#ifdef LOG_NNDEBUG +#define ALOGVV(...) ALOGV(__VA_ARGS__) +#else +#define ALOGVV(...) ((void)0) +#endif + +#include <gtest/gtest.h> +#include <gui/CpuConsumer.h> +#include <gui/SurfaceTextureClient.h> +#include <ui/GraphicBuffer.h> +#include <utils/String8.h> +#include <utils/Thread.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> + +#include <ui/FramebufferNativeWindow.h> + +namespace android { + +struct CpuConsumerTestParams { + uint32_t width; + uint32_t height; + int maxLockedBuffers; + PixelFormat format; +}; + +::std::ostream& operator<<(::std::ostream& os, const CpuConsumerTestParams& p) { + return os << "[ (" << p.width << ", " << p.height << "), B:" + << p.maxLockedBuffers << ", F:0x" + << ::std::hex << p.format << "]"; +} + +class CpuConsumerTest : public ::testing::TestWithParam<CpuConsumerTestParams> { +protected: + + virtual void SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + CpuConsumerTestParams params = GetParam(); + ALOGV("** Starting test %s (%d x %d, %d, 0x%x)", + test_info->name(), + params.width, params.height, + params.maxLockedBuffers, params.format); + mCC = new CpuConsumer(params.maxLockedBuffers); + String8 name("CpuConsumer_Under_Test"); + mCC->setName(name); + mSTC = new SurfaceTextureClient(mCC->getProducerInterface()); + mANW = mSTC; + } + + virtual void TearDown() { + mANW.clear(); + mSTC.clear(); + mCC.clear(); + } + + class FrameWaiter : public CpuConsumer::FrameAvailableListener { + public: + FrameWaiter(): + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mCondition.signal(); + } + + int mPendingFrames; + Mutex mMutex; + Condition mCondition; + }; + + // Note that SurfaceTexture will lose the notifications + // onBuffersReleased and onFrameAvailable as there is currently + // no way to forward the events. This DisconnectWaiter will not let the + // disconnect finish until finishDisconnect() is called. It will + // also block until a disconnect is called + class DisconnectWaiter : public BufferQueue::ConsumerListener { + public: + DisconnectWaiter () : + mWaitForDisconnect(false), + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mFrameCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mFrameCondition.signal(); + } + + virtual void onBuffersReleased() { + Mutex::Autolock lock(mMutex); + while (!mWaitForDisconnect) { + mDisconnectCondition.wait(mMutex); + } + } + + void finishDisconnect() { + Mutex::Autolock lock(mMutex); + mWaitForDisconnect = true; + mDisconnectCondition.signal(); + } + + private: + Mutex mMutex; + + bool mWaitForDisconnect; + Condition mDisconnectCondition; + + int mPendingFrames; + Condition mFrameCondition; + }; + + sp<CpuConsumer> mCC; + sp<SurfaceTextureClient> mSTC; + sp<ANativeWindow> mANW; +}; + +#define ASSERT_NO_ERROR(err, msg) \ + ASSERT_EQ(NO_ERROR, err) << msg << strerror(-err) + +void checkPixel(const CpuConsumer::LockedBuffer &buf, + uint32_t x, uint32_t y, uint32_t r, uint32_t g, uint32_t b) { + // Ignores components that don't exist for given pixel + switch(buf.format) { + case HAL_PIXEL_FORMAT_RAW_SENSOR: { + String8 msg; + uint16_t *bPtr = (uint16_t*)buf.data; + bPtr += y * buf.stride + x; + // GRBG Bayer mosaic; only check the matching channel + switch( ((y & 1) << 1) | (x & 1) ) { + case 0: // G + case 3: // G + EXPECT_EQ(g, *bPtr); + break; + case 1: // R + EXPECT_EQ(r, *bPtr); + break; + case 2: // B + EXPECT_EQ(b, *bPtr); + break; + } + break; + } + default: { + ADD_FAILURE() << "Unknown format for check:" << buf.format; + break; + } + } +} + +// Fill a YV12 buffer with a multi-colored checkerboard pattern +void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { + const int blockWidth = w > 16 ? w / 16 : 1; + const int blockHeight = h > 16 ? h / 16 : 1; + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int parityX = (x / blockWidth) & 1; + int parityY = (y / blockHeight) & 1; + unsigned char intensity = (parityX ^ parityY) ? 63 : 191; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; + if (x < w / 2 && y < h / 2) { + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; + if (x * 2 < w / 2 && y * 2 < h / 2) { + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = + intensity; + } + } + } + } +} + +// Fill a RAW sensor buffer with a multi-colored checkerboard pattern. +// Assumes GRBG mosaic ordering. Result should be a grid in a 2x2 pattern +// of [ R, B; G, W] +void fillBayerRawBuffer(uint8_t* buf, int w, int h, int stride) { + ALOGVV("fillBayerRawBuffer: %p with %d x %d, stride %d", buf, w, h ,stride); + // Blocks need to be even-width/height, aim for 8-wide otherwise + const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; + const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; + for (int y = 0; y < h; y+=2) { + uint16_t *bPtr1 = ((uint16_t*)buf) + stride*y; + uint16_t *bPtr2 = bPtr1 + stride; + for (int x = 0; x < w; x+=2) { + int blockX = (x / blockWidth ) & 1; + int blockY = (y / blockHeight) & 1; + unsigned short r = (blockX == blockY) ? 1000 : 200; + unsigned short g = blockY ? 1000: 200; + unsigned short b = blockX ? 1000: 200; + // GR row + *bPtr1++ = g; + *bPtr1++ = r; + // BG row + *bPtr2++ = b; + *bPtr2++ = g; + } + } + +} + +void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) { + uint32_t w = buf.width; + uint32_t h = buf.height; + const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; + const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; + const int blockRows = h / blockHeight; + const int blockCols = w / blockWidth; + + // Top-left square is red + checkPixel(buf, 0, 0, 1000, 200, 200); + checkPixel(buf, 1, 0, 1000, 200, 200); + checkPixel(buf, 0, 1, 1000, 200, 200); + checkPixel(buf, 1, 1, 1000, 200, 200); + + // One-right square is blue + checkPixel(buf, blockWidth, 0, 200, 200, 1000); + checkPixel(buf, blockWidth + 1, 0, 200, 200, 1000); + checkPixel(buf, blockWidth, 1, 200, 200, 1000); + checkPixel(buf, blockWidth + 1, 1, 200, 200, 1000); + + // One-down square is green + checkPixel(buf, 0, blockHeight, 200, 1000, 200); + checkPixel(buf, 1, blockHeight, 200, 1000, 200); + checkPixel(buf, 0, blockHeight + 1, 200, 1000, 200); + checkPixel(buf, 1, blockHeight + 1, 200, 1000, 200); + + // One-diag square is white + checkPixel(buf, blockWidth, blockHeight, 1000, 1000, 1000); + checkPixel(buf, blockWidth + 1, blockHeight, 1000, 1000, 1000); + checkPixel(buf, blockWidth, blockHeight + 1, 1000, 1000, 1000); + checkPixel(buf, blockWidth + 1, blockHeight + 1, 1000, 1000, 1000); + + // Test bottom-right pixel + const int maxBlockX = ((w-1) / blockWidth) & 0x1; + const int maxBlockY = ((w-1) / blockHeight) & 0x1; + unsigned short maxR = (maxBlockX == maxBlockY) ? 1000 : 200; + unsigned short maxG = maxBlockY ? 1000: 200; + unsigned short maxB = maxBlockX ? 1000: 200; + checkPixel(buf, w-1, h-1, maxR, maxG, maxB); +} + +// Fill a YV12 buffer with red outside a given rectangle and green inside it. +void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, + const android_native_rect_t& rect) { + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + bool inside = rect.left <= x && x < rect.right && + rect.top <= y && y < rect.bottom; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; + if (x < w / 2 && y < h / 2) { + bool inside = rect.left <= 2*x && 2*x < rect.right && + rect.top <= 2*y && 2*y < rect.bottom; + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; + buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = + inside ? 16 : 255; + } + } + } +} + +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { + const size_t PIXEL_SIZE = 4; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + for (int c = 0; c < 4; c++) { + int parityX = (x / (1 << (c+2))) & 1; + int parityY = (y / (1 << (c+2))) & 1; + buf[offset + c] = (parityX ^ parityY) ? 231 : 35; + } + } + } +} + +void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, + uint8_t g, uint8_t b, uint8_t a) { + const size_t PIXEL_SIZE = 4; + for (int y = 0; y < h; y++) { + for (int x = 0; x < h; x++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset + 0] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + +// Configures the ANativeWindow producer-side interface based on test parameters +void configureANW(const sp<ANativeWindow>& anw, + const CpuConsumerTestParams& params, + int maxBufferSlack) { + status_t err; + err = native_window_set_buffers_geometry(anw.get(), + params.width, params.height, params.format); + ASSERT_NO_ERROR(err, "set_buffers_geometry error: "); + + err = native_window_set_usage(anw.get(), + GRALLOC_USAGE_SW_WRITE_OFTEN); + ASSERT_NO_ERROR(err, "set_usage error: "); + + int minUndequeuedBuffers; + err = anw.get()->query(anw.get(), + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers); + ASSERT_NO_ERROR(err, "query error: "); + + ALOGVV("Setting buffer count to %d", + maxBufferSlack + 1 + minUndequeuedBuffers); + err = native_window_set_buffer_count(anw.get(), + maxBufferSlack + 1 + minUndequeuedBuffers); + ASSERT_NO_ERROR(err, "set_buffer_count error: "); + +} + +// Produce one frame of image data; assumes format and resolution configuration +// is already done. +void produceOneFrame(const sp<ANativeWindow>& anw, + const CpuConsumerTestParams& params, + int64_t timestamp, uint32_t *stride) { + status_t err; + ANativeWindowBuffer* anb; + ALOGVV("Dequeue buffer from %p", anw.get()); + err = anw->dequeueBuffer(anw.get(), &anb); + ASSERT_NO_ERROR(err, "dequeueBuffer error: "); + + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + ALOGVV("Lock buffer from %p", anw.get()); + err = anw->lockBuffer(anw.get(), buf->getNativeBuffer()); + ASSERT_NO_ERROR(err, "lockBuffer error: "); + + *stride = buf->getStride(); + uint8_t* img = NULL; + + ALOGVV("Lock buffer from %p for write", anw.get()); + err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + ASSERT_NO_ERROR(err, "lock error: "); + + switch (params.format) { + case HAL_PIXEL_FORMAT_YV12: + fillYV12Buffer(img, params.width, params.height, *stride); + break; + case HAL_PIXEL_FORMAT_RAW_SENSOR: + fillBayerRawBuffer(img, params.width, params.height, buf->getStride()); + break; + default: + FAIL() << "Unknown pixel format under test!"; + break; + } + ALOGVV("Unlock buffer from %p", anw.get()); + err = buf->unlock(); + ASSERT_NO_ERROR(err, "unlock error: "); + + ALOGVV("Set timestamp to %p", anw.get()); + err = native_window_set_buffers_timestamp(anw.get(), timestamp); + ASSERT_NO_ERROR(err, "set_buffers_timestamp error: "); + + ALOGVV("Queue buffer to %p", anw.get()); + err = anw->queueBuffer(anw.get(), buf->getNativeBuffer()); + ASSERT_NO_ERROR(err, "queueBuffer error:"); +}; + +TEST_P(CpuConsumerTest, FromCpuSingle) { + status_t err; + CpuConsumerTestParams params = GetParam(); + + // Set up + + ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, 1)); + + // Produce + + const int64_t time = 12345678L; + uint32_t stride; + ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time, + &stride)); + + // Consume + + CpuConsumer::LockedBuffer b; + err = mCC->lockNextBuffer(&b); + ASSERT_NO_ERROR(err, "getNextBuffer error: "); + + ASSERT_TRUE(b.data != NULL); + EXPECT_EQ(params.width, b.width); + EXPECT_EQ(params.height, b.height); + EXPECT_EQ(params.format, b.format); + EXPECT_EQ(stride, b.stride); + EXPECT_EQ(time, b.timestamp); + + checkBayerRawBuffer(b); + mCC->unlockBuffer(b); +} + +TEST_P(CpuConsumerTest, FromCpuManyInQueue) { + status_t err; + CpuConsumerTestParams params = GetParam(); + + const int numInQueue = 5; + // Set up + + ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, numInQueue)); + + // Produce + + const int64_t time[numInQueue] = { 1L, 2L, 3L, 4L, 5L}; + uint32_t stride[numInQueue]; + + for (int i = 0; i < numInQueue; i++) { + ALOGV("Producing frame %d", i); + ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time[i], + &stride[i])); + } + + // Consume + + for (int i = 0; i < numInQueue; i++) { + ALOGV("Consuming frame %d", i); + CpuConsumer::LockedBuffer b; + err = mCC->lockNextBuffer(&b); + ASSERT_NO_ERROR(err, "getNextBuffer error: "); + + ASSERT_TRUE(b.data != NULL); + EXPECT_EQ(params.width, b.width); + EXPECT_EQ(params.height, b.height); + EXPECT_EQ(params.format, b.format); + EXPECT_EQ(stride[i], b.stride); + EXPECT_EQ(time[i], b.timestamp); + + checkBayerRawBuffer(b); + + mCC->unlockBuffer(b); + } +} + +TEST_P(CpuConsumerTest, FromCpuLockMax) { + status_t err; + CpuConsumerTestParams params = GetParam(); + + // Set up + + ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, params.maxLockedBuffers + 1)); + + // Produce + + const int64_t time = 1234L; + uint32_t stride; + + for (int i = 0; i < params.maxLockedBuffers + 1; i++) { + ALOGV("Producing frame %d", i); + ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time, + &stride)); + } + + // Consume + + CpuConsumer::LockedBuffer *b = new CpuConsumer::LockedBuffer[params.maxLockedBuffers]; + for (int i = 0; i < params.maxLockedBuffers; i++) { + ALOGV("Locking frame %d", i); + err = mCC->lockNextBuffer(&b[i]); + ASSERT_NO_ERROR(err, "getNextBuffer error: "); + + ASSERT_TRUE(b[i].data != NULL); + EXPECT_EQ(params.width, b[i].width); + EXPECT_EQ(params.height, b[i].height); + EXPECT_EQ(params.format, b[i].format); + EXPECT_EQ(stride, b[i].stride); + EXPECT_EQ(time, b[i].timestamp); + + checkBayerRawBuffer(b[i]); + } + + ALOGV("Locking frame %d (too many)", params.maxLockedBuffers); + CpuConsumer::LockedBuffer bTooMuch; + err = mCC->lockNextBuffer(&bTooMuch); + ASSERT_TRUE(err == INVALID_OPERATION) << "Allowing too many locks"; + + ALOGV("Unlocking frame 0"); + err = mCC->unlockBuffer(b[0]); + ASSERT_NO_ERROR(err, "Could not unlock buffer 0: "); + + ALOGV("Locking frame %d (should work now)", params.maxLockedBuffers); + err = mCC->lockNextBuffer(&bTooMuch); + ASSERT_NO_ERROR(err, "Did not allow new lock after unlock"); + + ASSERT_TRUE(bTooMuch.data != NULL); + EXPECT_EQ(params.width, bTooMuch.width); + EXPECT_EQ(params.height, bTooMuch.height); + EXPECT_EQ(params.format, bTooMuch.format); + EXPECT_EQ(stride, bTooMuch.stride); + EXPECT_EQ(time, bTooMuch.timestamp); + + checkBayerRawBuffer(bTooMuch); + + ALOGV("Unlocking extra buffer"); + err = mCC->unlockBuffer(bTooMuch); + ASSERT_NO_ERROR(err, "Could not unlock extra buffer: "); + + ALOGV("Locking frame %d (no more available)", params.maxLockedBuffers + 1); + err = mCC->lockNextBuffer(&b[0]); + ASSERT_EQ(BAD_VALUE, err) << "Not out of buffers somehow"; + + for (int i = 1; i < params.maxLockedBuffers; i++) { + mCC->unlockBuffer(b[i]); + } + + delete[] b; + +} + +CpuConsumerTestParams rawTestSets[] = { + { 512, 512, 1, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 512, 512, 3, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 2608, 1960, 1, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 2608, 1960, 3, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 100, 100, 1, HAL_PIXEL_FORMAT_RAW_SENSOR}, + { 100, 100, 3, HAL_PIXEL_FORMAT_RAW_SENSOR} +}; + +INSTANTIATE_TEST_CASE_P(RawTests, + CpuConsumerTest, + ::testing::ValuesIn(rawTestSets)); + +} // namespace android diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index cad7720..db17546 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -118,7 +118,7 @@ ZipFileRO::~ZipFileRO() { */ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const { - long ent = ((long) entry) - kZipEntryAdj; + long ent = ((intptr_t) entry) - kZipEntryAdj; if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); return -1; @@ -459,7 +459,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const for (int ent = 0; ent < mHashTableSize; ent++) { if (mHashTable[ent].name != NULL) { if (idx-- == 0) - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); } } diff --git a/opengl/specs/EGL_ANDROID_fence_sync.txt b/opengl/specs/EGL_ANDROID_fence_sync.txt new file mode 100644 index 0000000..bb618c9 --- /dev/null +++ b/opengl/specs/EGL_ANDROID_fence_sync.txt @@ -0,0 +1,213 @@ +Name + + ANDROID_fence_sync + +Name Strings + + EGL_ANDROID_fence_sync + +Contributors + + Jamie Gennis + +Contact + + Jamie Gennis, Google Inc. (jgennis 'at' google.com) + +Status + + Draft. + +Version + + Version 1, May 29, 2012 + +Number + + EGL Extension #XXX + +Dependencies + + Requires EGL 1.1 + + This extension is written against the wording of the EGL 1.2 Specification + + EGL_KHR_fence_sync is required. + +Overview + + This extension enables the creation of EGL fence sync objects that are + associated with an Android Sync HAL fence object. These EGL fence sync + objects have nearly identical semantics to those defined by the + KHR_fence_sync extension, except that they have an additional attribute + storing the file descriptor referring to the native Android fence object. + +New Types + + None. + +New Procedures and Functions + + None. + +New Tokens + + Accepted by the <type> parameter of eglCreateSyncKHR, and returned + in <value> when eglGetSyncAttribKHR is called with <attribute> + EGL_SYNC_TYPE_KHR: + + EGL_SYNC_ANDROID_FENCE_ANDROID 0x3144 + + Accepted by the <attribute> parameter of eglGetSyncAttribKHR: + + EGL_SYNC_FENCE_FD_ANDROID 0x3145 + + Returned in <value> when eglGetSyncAttribKHR is called with <attribute> + EGL_SYNC_CONDITION_KHR: + + EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID 0x3146 + +Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors) + + Add the following after the sixth paragraph of Section 3.8.1 (Sync + Objects), added by KHR_fence_sync + + "If <type> is EGL_SYNC_ANDROID_FENCE_ANDROID, an EGL Android fence sync + object is created. In this case the EGL_SYNC_FENCE_FD_ANDROID attribute may + optionally be specified. If this attribute is specified, it must be set to + a file descriptor that refers to a native Android fence object. + + The default values for the EGL Android fence sync object attributes are as + follows: + + Attribute Name Initial Attribute Value(s) + --------------- -------------------------- + EGL_SYNC_TYPE_KHR EGL_SYNC_ANDROID_FENCE_ANDROID + EGL_SYNC_STATUS_KHR EGL_UNSIGNALED_KHR + EGL_SYNC_CONDITION_KHR EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR + + Upon creation of an EGL Android fence sync object, the + EGL_SYNC_FENCE_FD_ANDROID attribute is set to a newly generated file + descriptor that refers to a native Android fence object. If the + EGL_SYNC_FENCE_FD_ANDROID attribute is specified in the eglCreateSyncKHR + call then the generated file descriptor refers to the same native Android + fence object as the file descriptor passed to eglCreateSyncKHR. Note, + however, that the value of the sync object attribute is *not* the same file + descriptor as the one passed to eglCreateSyncKHR - it simply refers to the + same underlying native Android fence object. Additionally, if the + EGL_SYNC_FENCE_FD_ANDROID attribute is specified then the + EGL_SYNC_CONDITION_KHR attribute is set to + EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID and the EGL_SYNC_STATUS_KHR + attribute is set to reflect the signal status of the native Android fence + object." + + Modify Section 3.8.1 (Sync Objects), added by KHR_fence_sync, starting at + the seventh paragraph + + "When a fence sync object is created or when an EGL Android fence sync + object is created without specifying the EGL_SYNC_FENCE_FD_ANDROID + attribute, eglCreateSyncKHR also inserts a fence command into the command + stream of the bound client API's current context (i.e., the context + returned by eglGetCurrentContext), and associates it with the newly created + sync object. + + When the condition of the sync object is satisfied by the fence command, + the sync is signaled by the associated client API context, causing any + eglClientWaitSyncKHR commands (see below) blocking on <sync> to unblock. If + the sync object is an EGL Android fence sync object then the native Android + fence object is also signaled when the condition is satisfied. The + EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR condition is satisfied by completion + of the fence command corresponding to the sync object and all preceding + commands in the associated client API context's command stream. The sync + object will not be signaled until all effects from these commands on the + client API's internal and framebuffer state are fully realized. No other + state is affected by execution of the fence command. + + The EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID condition is satisfied by the + signaling of the native Android fence object. When this happens any + eglClientWaitSyncKHR commands blocking on <sync> unblock." + + Modify the list of eglCreateSyncKHR errors in Section 3.8.1 (Sync Objects), + added by KHR_fence_sync + + "Errors + ------ + + * If <dpy> is not the name of a valid, initialized EGLDisplay, + EGL_NO_SYNC_KHR is returned and an EGL_BAD_DISPLAY error is + generated. + * If <type> is EGL_SYNC_FENCE_KHR and <attrib_list> is neither NULL nor + empty (containing only EGL_NONE), EGL_NO_SYNC_KHR is returned and an + EGL_BAD_ATTRIBUTE error is generated. + * If <type> is EGL_SYNC_ANDROID_FENCE_ANDROID and <attrib_list> contains + an attribute other than EGL_SYNC_FENCE_FD_ANDROID, EGL_NO_SYNC_KHR is + returned and an EGL_BAD_ATTRIBUTE error is generated. + * If <type> is not a supported type of sync object, + EGL_NO_SYNC_KHR is returned and an EGL_BAD_ATTRIBUTE error is + generated. + * If <type> is EGL_SYNC_FENCE_KHR or EGL_SYNC_ANDROID_FENCE_ANDROID and + no context is current for the bound API (i.e., eglGetCurrentContext + returns EGL_NO_CONTEXT), EGL_NO_SYNC_KHR is returned and an + EGL_BAD_MATCH error is generated. + * If <type> is EGL_SYNC_FENCE_KHR or EGL_SYNC_ANDROID_FENCE_ANDROID and + <dpy> does not match the EGLDisplay of the currently bound context for + the currently bound client API (the EGLDisplay returned by + eglGetCurrentDisplay()) then EGL_NO_SYNC_KHR is returned and an + EGL_BAD_MATCH error is generated. + * If <type> is EGL_SYNC_FENCE_KHR or EGL_SYNC_ANDROID_FENCE_ANDROID and + the currently bound client API does not support the client API + extension indicating it can place fence commands, then EGL_NO_SYNC_KHR + is returned and an EGL_BAD_MATCH error is generated." + + Modify table 3.cc in Section 3.8.1 (Sync Objects), added by KHR_fence_sync + + " + Attribute Description Supported Sync Objects + ----------------- ----------------------- ---------------------- + EGL_SYNC_TYPE_KHR Type of the sync object All + EGL_SYNC_STATUS_KHR Status of the sync object All + EGL_SYNC_CONDITION_KHR Signaling condition EGL_SYNC_FENCE_KHR and + EGL_SYNC_ANDROID_FENCE_ANDROID only + EGL_SYNC_FENCE_FD_ANDROID Native Android fence EGL_SYNC_ANDROID_FENCE_ANDROID only + object file descriptor + " + + Modify the second paragraph description of eglDestroySyncKHR in Section + 3.8.1 (Sync Objects), added by KHR_fence_sync + + "If no errors are generated, EGL_TRUE is returned, and <sync> will no + longer be the handle of a valid sync object. Additionally, if <sync> is an + EGL Android fence sync object then the file descriptor stored in the + EGL_SYNC_FENCE_FD_ANDROID attribute is closed and is no longer a valid file + descriptor." + +Issues + + 1. Should EGLSyncKHR objects that wrap Android fence objects use the + EGL_SYNC_FENCE_KHR type? + + RESOLVED: A new sync object type will be added. + + We don't want to require all EGL fence sync objects to wrap Android fence + objects, so we need some way to tell the EGL implementation at sync object + creation whether the sync object should support querying the Android fence + FD attribute. We could do this with either a new sync object type or with a + boolean attribute. It might be nice to pick up future signaling conditions + that might be added for fence sync objects, but there may be things that + get added that don't make sense in the context of Android fence objects. + + 2. Who is responsible for dup'ing the Android fence file descriptors? + + RESOLVED: The recipient of a file descriptor is responsible for dup'ing it + if it wishes to maintain a reference to the Android fence object. + + This means that when eglCreateSyncKHR is called with an existing file + descriptor, the EGL implementation must dup it. On the other hand, when + eglGetSyncAttribKHR is called to query the file descriptor, it is the + responsibility of the caller to dup the file descriptor if it wishes to + maintain a reference that will outlast the EGLSyncKHR object. + +Revision History + +#1 (Jamie Gennis, May 29, 2012) + - Initial draft. diff --git a/opengl/specs/README b/opengl/specs/README index 16b278f..af3f165 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -10,4 +10,7 @@ for use by Android extensions. 0x3141 (unused) 0x3142 EGL_ANDROID_recordable 0x3143 EGL_VERSION_HW_ANDROID (internal use) -0x3144 - 0x314F (unused) +0x3144 EGL_SYNC_ANDROID_FENCE_ANDROID +0x3145 EGL_SYNC_FENCE_FD_ANDROID +0x3146 EGL_SYNC_ANDROID_FENCE_SIGNALED_ANDROID +0x3147 - 0x314F (unused) |