mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1098970 - Part 1: Add new LL native files. r=sotaro
Copied from aosp/framework/native/gui.
This commit is contained in:
parent
5cfd1b5156
commit
33b88b3371
192
widget/gonk/nativewindow/GonkBufferQueueLL/BufferItem.cpp
Normal file
192
widget/gonk/nativewindow/GonkBufferQueueLL/BufferItem.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#include <gui/BufferItem.h>
|
||||
|
||||
#include <ui/Fence.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <system/window.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
BufferItem::BufferItem() :
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mIsAutoTimestamp(false),
|
||||
mFrameNumber(0),
|
||||
mSlot(INVALID_BUFFER_SLOT),
|
||||
mIsDroppable(false),
|
||||
mAcquireCalled(false),
|
||||
mTransformToDisplayInverse(false) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
|
||||
BufferItem::operator IGraphicBufferConsumer::BufferItem() const {
|
||||
IGraphicBufferConsumer::BufferItem bufferItem;
|
||||
bufferItem.mGraphicBuffer = mGraphicBuffer;
|
||||
bufferItem.mFence = mFence;
|
||||
bufferItem.mCrop = mCrop;
|
||||
bufferItem.mTransform = mTransform;
|
||||
bufferItem.mScalingMode = mScalingMode;
|
||||
bufferItem.mTimestamp = mTimestamp;
|
||||
bufferItem.mIsAutoTimestamp = mIsAutoTimestamp;
|
||||
bufferItem.mFrameNumber = mFrameNumber;
|
||||
bufferItem.mBuf = mSlot;
|
||||
bufferItem.mIsDroppable = mIsDroppable;
|
||||
bufferItem.mAcquireCalled = mAcquireCalled;
|
||||
bufferItem.mTransformToDisplayInverse = mTransformToDisplayInverse;
|
||||
return bufferItem;
|
||||
}
|
||||
|
||||
size_t BufferItem::getPodSize() const {
|
||||
size_t c = sizeof(mCrop) +
|
||||
sizeof(mTransform) +
|
||||
sizeof(mScalingMode) +
|
||||
sizeof(mTimestamp) +
|
||||
sizeof(mIsAutoTimestamp) +
|
||||
sizeof(mFrameNumber) +
|
||||
sizeof(mSlot) +
|
||||
sizeof(mIsDroppable) +
|
||||
sizeof(mAcquireCalled) +
|
||||
sizeof(mTransformToDisplayInverse);
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t BufferItem::getFlattenedSize() const {
|
||||
size_t c = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
c += mGraphicBuffer->getFlattenedSize();
|
||||
FlattenableUtils::align<4>(c);
|
||||
}
|
||||
if (mFence != 0) {
|
||||
c += mFence->getFlattenedSize();
|
||||
FlattenableUtils::align<4>(c);
|
||||
}
|
||||
return sizeof(int32_t) + c + getPodSize();
|
||||
}
|
||||
|
||||
size_t BufferItem::getFdCount() const {
|
||||
size_t c = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
c += mGraphicBuffer->getFdCount();
|
||||
}
|
||||
if (mFence != 0) {
|
||||
c += mFence->getFdCount();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
status_t BufferItem::flatten(
|
||||
void*& buffer, size_t& size, int*& fds, size_t& count) const {
|
||||
|
||||
// make sure we have enough space
|
||||
if (count < BufferItem::getFlattenedSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
// content flags are stored first
|
||||
uint32_t& flags = *static_cast<uint32_t*>(buffer);
|
||||
|
||||
// advance the pointer
|
||||
FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
|
||||
|
||||
flags = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
flags |= 1;
|
||||
}
|
||||
if (mFence != 0) {
|
||||
status_t err = mFence->flatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
flags |= 2;
|
||||
}
|
||||
|
||||
// check we have enough space (in case flattening the fence/graphicbuffer lied to us)
|
||||
if (size < getPodSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
FlattenableUtils::write(buffer, size, mCrop);
|
||||
FlattenableUtils::write(buffer, size, mTransform);
|
||||
FlattenableUtils::write(buffer, size, mScalingMode);
|
||||
FlattenableUtils::write(buffer, size, mTimestamp);
|
||||
FlattenableUtils::write(buffer, size, mIsAutoTimestamp);
|
||||
FlattenableUtils::write(buffer, size, mFrameNumber);
|
||||
FlattenableUtils::write(buffer, size, mSlot);
|
||||
FlattenableUtils::write(buffer, size, mIsDroppable);
|
||||
FlattenableUtils::write(buffer, size, mAcquireCalled);
|
||||
FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferItem::unflatten(
|
||||
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
|
||||
|
||||
if (size < sizeof(uint32_t))
|
||||
return NO_MEMORY;
|
||||
|
||||
uint32_t flags = 0;
|
||||
FlattenableUtils::read(buffer, size, flags);
|
||||
|
||||
if (flags & 1) {
|
||||
mGraphicBuffer = new GraphicBuffer();
|
||||
status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
}
|
||||
|
||||
if (flags & 2) {
|
||||
mFence = new Fence();
|
||||
status_t err = mFence->unflatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
}
|
||||
|
||||
// check we have enough space
|
||||
if (size < getPodSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
FlattenableUtils::read(buffer, size, mCrop);
|
||||
FlattenableUtils::read(buffer, size, mTransform);
|
||||
FlattenableUtils::read(buffer, size, mScalingMode);
|
||||
FlattenableUtils::read(buffer, size, mTimestamp);
|
||||
FlattenableUtils::read(buffer, size, mIsAutoTimestamp);
|
||||
FlattenableUtils::read(buffer, size, mFrameNumber);
|
||||
FlattenableUtils::read(buffer, size, mSlot);
|
||||
FlattenableUtils::read(buffer, size, mIsDroppable);
|
||||
FlattenableUtils::read(buffer, size, mAcquireCalled);
|
||||
FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const char* BufferItem::scalingModeName(uint32_t scalingMode) {
|
||||
switch (scalingMode) {
|
||||
case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
103
widget/gonk/nativewindow/GonkBufferQueueLL/BufferItem.h
Normal file
103
widget/gonk/nativewindow/GonkBufferQueueLL/BufferItem.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2014 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_BUFFERITEM_H
|
||||
#define ANDROID_GUI_BUFFERITEM_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <gui/IGraphicBufferConsumer.h>
|
||||
|
||||
#include <ui/Rect.h>
|
||||
|
||||
#include <utils/Flattenable.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class Fence;
|
||||
class GraphicBuffer;
|
||||
|
||||
class BufferItem : public Flattenable<BufferItem> {
|
||||
friend class Flattenable<BufferItem>;
|
||||
size_t getPodSize() const;
|
||||
size_t getFlattenedSize() const;
|
||||
size_t getFdCount() const;
|
||||
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
|
||||
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
|
||||
|
||||
public:
|
||||
// The default value of mBuf, used to indicate this doesn't correspond to a slot.
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
BufferItem();
|
||||
operator IGraphicBufferConsumer::BufferItem() const;
|
||||
|
||||
static const char* scalingModeName(uint32_t scalingMode);
|
||||
|
||||
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
|
||||
// if the buffer in this slot has been acquired in the past (see
|
||||
// BufferSlot.mAcquireCalled).
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mFence is a fence that will signal when the buffer is idle.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// mCrop is the current crop rectangle for this buffer slot.
|
||||
Rect mCrop;
|
||||
|
||||
// mTransform is the current transform flags for this buffer slot.
|
||||
// refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
|
||||
uint32_t mTransform;
|
||||
|
||||
// mScalingMode is the current scaling mode for this buffer slot.
|
||||
// refer to NATIVE_WINDOW_SCALING_* in <window.h>
|
||||
uint32_t mScalingMode;
|
||||
|
||||
// mTimestamp is the current timestamp for this buffer slot. This gets
|
||||
// to set by queueBuffer each time this slot is queued. This value
|
||||
// is guaranteed to be monotonically increasing for each newly
|
||||
// acquired buffer.
|
||||
int64_t mTimestamp;
|
||||
|
||||
// mIsAutoTimestamp indicates whether mTimestamp was generated
|
||||
// automatically when the buffer was queued.
|
||||
bool mIsAutoTimestamp;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot.
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
|
||||
int mSlot;
|
||||
|
||||
// mIsDroppable whether this buffer was queued with the
|
||||
// property that it can be replaced by a new buffer for the purpose of
|
||||
// making sure dequeueBuffer() won't block.
|
||||
// i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
|
||||
// was queued.
|
||||
bool mIsDroppable;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
|
||||
// Indicates this buffer must be transformed by the inverse transform of the screen
|
||||
// it is displayed onto. This is applied after mTransform.
|
||||
bool mTransformToDisplayInverse;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
79
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueue.cpp
Normal file
79
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueue.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 "BufferQueue"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <gui/BufferQueue.h>
|
||||
#include <gui/BufferQueueConsumer.h>
|
||||
#include <gui/BufferQueueCore.h>
|
||||
#include <gui/BufferQueueProducer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
|
||||
const wp<ConsumerListener>& consumerListener):
|
||||
mConsumerListener(consumerListener) {}
|
||||
|
||||
BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
|
||||
|
||||
void BufferQueue::ProxyConsumerListener::onFrameAvailable() {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onFrameAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onBuffersReleased();
|
||||
}
|
||||
}
|
||||
|
||||
void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
|
||||
sp<ConsumerListener> listener(mConsumerListener.promote());
|
||||
if (listener != NULL) {
|
||||
listener->onSidebandStreamChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
|
||||
sp<IGraphicBufferConsumer>* outConsumer,
|
||||
const sp<IGraphicBufferAlloc>& allocator) {
|
||||
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
|
||||
"BufferQueue: outProducer must not be NULL");
|
||||
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
|
||||
"BufferQueue: outConsumer must not be NULL");
|
||||
|
||||
sp<BufferQueueCore> core(new BufferQueueCore(allocator));
|
||||
LOG_ALWAYS_FATAL_IF(core == NULL,
|
||||
"BufferQueue: failed to create BufferQueueCore");
|
||||
|
||||
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
|
||||
LOG_ALWAYS_FATAL_IF(producer == NULL,
|
||||
"BufferQueue: failed to create BufferQueueProducer");
|
||||
|
||||
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
|
||||
LOG_ALWAYS_FATAL_IF(consumer == NULL,
|
||||
"BufferQueue: failed to create BufferQueueConsumer");
|
||||
|
||||
*outProducer = producer;
|
||||
*outConsumer = consumer;
|
||||
}
|
||||
|
||||
}; // namespace android
|
88
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueue.h
Normal file
88
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueue.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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_BUFFERQUEUE_H
|
||||
#define ANDROID_GUI_BUFFERQUEUE_H
|
||||
|
||||
#include <gui/BufferQueueDefs.h>
|
||||
#include <gui/IGraphicBufferConsumer.h>
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
#include <gui/IConsumerListener.h>
|
||||
|
||||
// These are only required to keep other parts of the framework with incomplete
|
||||
// dependencies building successfully
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class BufferQueue {
|
||||
public:
|
||||
// BufferQueue will keep track of at most this value of buffers.
|
||||
// Attempts at runtime to increase the number of buffers past this will fail.
|
||||
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
|
||||
// Used as a placeholder slot# when the value isn't pointing to an existing buffer.
|
||||
enum { INVALID_BUFFER_SLOT = IGraphicBufferConsumer::BufferItem::INVALID_BUFFER_SLOT };
|
||||
// Alias to <IGraphicBufferConsumer.h> -- please scope from there in future code!
|
||||
enum {
|
||||
NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
|
||||
PRESENT_LATER = IGraphicBufferConsumer::PRESENT_LATER,
|
||||
};
|
||||
|
||||
// When in async mode we reserve two slots in order to guarantee that the
|
||||
// producer and consumer can run asynchronously.
|
||||
enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
|
||||
|
||||
// for backward source compatibility
|
||||
typedef ::android::ConsumerListener ConsumerListener;
|
||||
typedef IGraphicBufferConsumer::BufferItem BufferItem;
|
||||
|
||||
// ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
|
||||
// reference to the actual consumer object. It forwards all calls to that
|
||||
// consumer object so long as it exists.
|
||||
//
|
||||
// This class exists to avoid having a circular reference between the
|
||||
// BufferQueue object and the consumer object. The reason this can't be a weak
|
||||
// reference in the BufferQueue class is because we're planning to expose the
|
||||
// consumer side of a BufferQueue as a binder interface, which doesn't support
|
||||
// weak references.
|
||||
class ProxyConsumerListener : public BnConsumerListener {
|
||||
public:
|
||||
ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
|
||||
virtual ~ProxyConsumerListener();
|
||||
virtual void onFrameAvailable();
|
||||
virtual void onBuffersReleased();
|
||||
virtual void onSidebandStreamChanged();
|
||||
private:
|
||||
// mConsumerListener is a weak reference to the IConsumerListener. This is
|
||||
// the raison d'etre of ProxyConsumerListener.
|
||||
wp<ConsumerListener> mConsumerListener;
|
||||
};
|
||||
|
||||
// BufferQueue manages a pool of gralloc memory slots to be used by
|
||||
// producers and consumers. allocator is used to allocate all the
|
||||
// needed gralloc buffers.
|
||||
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
|
||||
sp<IGraphicBufferConsumer>* outConsumer,
|
||||
const sp<IGraphicBufferAlloc>& allocator = NULL);
|
||||
|
||||
private:
|
||||
BufferQueue(); // Create through createBufferQueue
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_BUFFERQUEUE_H
|
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOG_TAG "BufferQueueConsumer"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <gui/BufferItem.h>
|
||||
#include <gui/BufferQueueConsumer.h>
|
||||
#include <gui/BufferQueueCore.h>
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IProducerListener.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
|
||||
mCore(core),
|
||||
mSlots(core->mSlots),
|
||||
mConsumerName() {}
|
||||
|
||||
BufferQueueConsumer::~BufferQueueConsumer() {}
|
||||
|
||||
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
|
||||
nsecs_t expectedPresent) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
// Check that the consumer doesn't currently have the maximum number of
|
||||
// buffers acquired. We allow the max buffer count to be exceeded by one
|
||||
// buffer so that the consumer can successfully set up the newly acquired
|
||||
// buffer before releasing the old one.
|
||||
int numAcquiredBuffers = 0;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
|
||||
++numAcquiredBuffers;
|
||||
}
|
||||
}
|
||||
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
|
||||
BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
|
||||
numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// Check if the queue is empty.
|
||||
// In asynchronous mode the list is guaranteed to be one buffer deep,
|
||||
// while in synchronous mode we use the oldest buffer.
|
||||
if (mCore->mQueue.empty()) {
|
||||
return NO_BUFFER_AVAILABLE;
|
||||
}
|
||||
|
||||
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
|
||||
|
||||
// If expectedPresent is specified, we may not want to return a buffer yet.
|
||||
// If it's specified and there's more than one buffer queued, we may want
|
||||
// to drop a buffer.
|
||||
if (expectedPresent != 0) {
|
||||
const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
|
||||
|
||||
// The 'expectedPresent' argument indicates when the buffer is expected
|
||||
// to be presented on-screen. If the buffer's desired present time is
|
||||
// earlier (less) than expectedPresent -- meaning it will be displayed
|
||||
// on time or possibly late if we show it as soon as possible -- we
|
||||
// acquire and return it. If we don't want to display it until after the
|
||||
// expectedPresent time, we return PRESENT_LATER without acquiring it.
|
||||
//
|
||||
// To be safe, we don't defer acquisition if expectedPresent is more
|
||||
// than one second in the future beyond the desired present time
|
||||
// (i.e., we'd be holding the buffer for a long time).
|
||||
//
|
||||
// NOTE: Code assumes monotonic time values from the system clock
|
||||
// are positive.
|
||||
|
||||
// Start by checking to see if we can drop frames. We skip this check if
|
||||
// the timestamps are being auto-generated by Surface. If the app isn't
|
||||
// generating timestamps explicitly, it probably doesn't want frames to
|
||||
// be discarded based on them.
|
||||
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
|
||||
// If entry[1] is timely, drop entry[0] (and repeat). We apply an
|
||||
// additional criterion here: we only drop the earlier buffer if our
|
||||
// desiredPresent falls within +/- 1 second of the expected present.
|
||||
// Otherwise, bogus desiredPresent times (e.g., 0 or a small
|
||||
// relative timestamp), which normally mean "ignore the timestamp
|
||||
// and acquire immediately", would cause us to drop frames.
|
||||
//
|
||||
// We may want to add an additional criterion: don't drop the
|
||||
// earlier buffer if entry[1]'s fence hasn't signaled yet.
|
||||
const BufferItem& bufferItem(mCore->mQueue[1]);
|
||||
nsecs_t desiredPresent = bufferItem.mTimestamp;
|
||||
if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
|
||||
desiredPresent > expectedPresent) {
|
||||
// This buffer is set to display in the near future, or
|
||||
// desiredPresent is garbage. Either way we don't want to drop
|
||||
// the previous buffer just to get this on the screen sooner.
|
||||
BQ_LOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
|
||||
PRId64 " (%" PRId64 ") now=%" PRId64,
|
||||
desiredPresent, expectedPresent,
|
||||
desiredPresent - expectedPresent,
|
||||
systemTime(CLOCK_MONOTONIC));
|
||||
break;
|
||||
}
|
||||
|
||||
BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
|
||||
" size=%zu",
|
||||
desiredPresent, expectedPresent, mCore->mQueue.size());
|
||||
if (mCore->stillTracking(front)) {
|
||||
// Front buffer is still in mSlots, so mark the slot as free
|
||||
mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
|
||||
}
|
||||
mCore->mQueue.erase(front);
|
||||
front = mCore->mQueue.begin();
|
||||
}
|
||||
|
||||
// See if the front buffer is due
|
||||
nsecs_t desiredPresent = front->mTimestamp;
|
||||
if (desiredPresent > expectedPresent &&
|
||||
desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
|
||||
BQ_LOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64
|
||||
" (%" PRId64 ") now=%" PRId64,
|
||||
desiredPresent, expectedPresent,
|
||||
desiredPresent - expectedPresent,
|
||||
systemTime(CLOCK_MONOTONIC));
|
||||
return PRESENT_LATER;
|
||||
}
|
||||
|
||||
BQ_LOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " "
|
||||
"(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent,
|
||||
desiredPresent - expectedPresent,
|
||||
systemTime(CLOCK_MONOTONIC));
|
||||
}
|
||||
|
||||
int slot = front->mSlot;
|
||||
*outBuffer = *front;
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
|
||||
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
|
||||
slot, front->mFrameNumber, front->mGraphicBuffer->handle);
|
||||
// If the front buffer is still being tracked, update its slot state
|
||||
if (mCore->stillTracking(front)) {
|
||||
mSlots[slot].mAcquireCalled = true;
|
||||
mSlots[slot].mNeedsCleanupOnRelease = false;
|
||||
mSlots[slot].mBufferState = BufferSlot::ACQUIRED;
|
||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||
}
|
||||
|
||||
// If the buffer has previously been acquired by the consumer, set
|
||||
// mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
|
||||
// on the consumer side
|
||||
if (outBuffer->mAcquireCalled) {
|
||||
outBuffer->mGraphicBuffer = NULL;
|
||||
}
|
||||
|
||||
mCore->mQueue.erase(front);
|
||||
|
||||
// We might have freed a slot while dropping old buffers, or the producer
|
||||
// may be blocked waiting for the number of buffers in the queue to
|
||||
// decrease.
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::detachBuffer(int slot) {
|
||||
ATRACE_CALL();
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
BQ_LOGV("detachBuffer(C): slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)",
|
||||
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) {
|
||||
BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->freeBufferLocked(slot);
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::attachBuffer(int* outSlot,
|
||||
const sp<android::GraphicBuffer>& buffer) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outSlot == NULL) {
|
||||
BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
|
||||
return BAD_VALUE;
|
||||
} else if (buffer == NULL) {
|
||||
BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
// Make sure we don't have too many acquired buffers and find a free slot
|
||||
// to put the buffer into (the oldest if there are multiple).
|
||||
int numAcquiredBuffers = 0;
|
||||
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
|
||||
++numAcquiredBuffers;
|
||||
} else if (mSlots[s].mBufferState == BufferSlot::FREE) {
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
|
||||
mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
|
||||
found = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
|
||||
BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d "
|
||||
"(max %d)", numAcquiredBuffers,
|
||||
mCore->mMaxAcquiredBufferCount);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
BQ_LOGE("attachBuffer(P): could not find free buffer slot");
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
ATRACE_BUFFER_INDEX(*outSlot);
|
||||
BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot);
|
||||
|
||||
mSlots[*outSlot].mGraphicBuffer = buffer;
|
||||
mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
|
||||
mSlots[*outSlot].mAttachedByConsumer = true;
|
||||
mSlots[*outSlot].mNeedsCleanupOnRelease = false;
|
||||
mSlots[*outSlot].mFence = Fence::NO_FENCE;
|
||||
mSlots[*outSlot].mFrameNumber = 0;
|
||||
|
||||
// mAcquireCalled tells BufferQueue that it doesn't need to send a valid
|
||||
// GraphicBuffer pointer on the next acquireBuffer call, which decreases
|
||||
// Binder traffic by not un/flattening the GraphicBuffer. However, it
|
||||
// requires that the consumer maintain a cached copy of the slot <--> buffer
|
||||
// mappings, which is why the consumer doesn't need the valid pointer on
|
||||
// acquire.
|
||||
//
|
||||
// The StreamSplitter is one of the primary users of the attach/detach
|
||||
// logic, and while it is running, all buffers it acquires are immediately
|
||||
// detached, and all buffers it eventually releases are ones that were
|
||||
// attached (as opposed to having been obtained from acquireBuffer), so it
|
||||
// doesn't make sense to maintain the slot/buffer mappings, which would
|
||||
// become invalid for every buffer during detach/attach. By setting this to
|
||||
// false, the valid GraphicBuffer pointer will always be sent with acquire
|
||||
// for attached buffers.
|
||||
mSlots[*outSlot].mAcquireCalled = false;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
|
||||
const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
|
||||
EGLSyncKHR eglFence) {
|
||||
ATRACE_CALL();
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
|
||||
releaseFence == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<IProducerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
// If the frame number has changed because the buffer has been reallocated,
|
||||
// we can ignore this releaseBuffer for the old buffer
|
||||
if (frameNumber != mSlots[slot].mFrameNumber) {
|
||||
return STALE_BUFFER_SLOT;
|
||||
}
|
||||
|
||||
// Make sure this buffer hasn't been queued while acquired by the consumer
|
||||
BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
|
||||
while (current != mCore->mQueue.end()) {
|
||||
if (current->mSlot == slot) {
|
||||
BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
|
||||
"currently queued", slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
|
||||
mSlots[slot].mEglDisplay = eglDisplay;
|
||||
mSlots[slot].mEglFence = eglFence;
|
||||
mSlots[slot].mFence = releaseFence;
|
||||
mSlots[slot].mBufferState = BufferSlot::FREE;
|
||||
listener = mCore->mConnectedProducerListener;
|
||||
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
|
||||
} else if (mSlots[slot].mNeedsCleanupOnRelease) {
|
||||
BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
mSlots[slot].mNeedsCleanupOnRelease = false;
|
||||
return STALE_BUFFER_SLOT;
|
||||
} else {
|
||||
BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
|
||||
"but its state was %d", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onBufferReleased();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::connect(
|
||||
const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (consumerListener == NULL) {
|
||||
BQ_LOGE("connect(C): consumerListener may not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
BQ_LOGV("connect(C): controlledByApp=%s",
|
||||
controlledByApp ? "true" : "false");
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("connect(C): BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
mCore->mConsumerListener = consumerListener;
|
||||
mCore->mConsumerControlledByApp = controlledByApp;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::disconnect() {
|
||||
ATRACE_CALL();
|
||||
|
||||
BQ_LOGV("disconnect(C)");
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mConsumerListener == NULL) {
|
||||
BQ_LOGE("disconnect(C): no consumer is connected");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->mIsAbandoned = true;
|
||||
mCore->mConsumerListener = NULL;
|
||||
mCore->mQueue.clear();
|
||||
mCore->freeAllBuffersLocked();
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outSlotMask == NULL) {
|
||||
BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
uint64_t mask = 0;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (!mSlots[s].mAcquireCalled) {
|
||||
mask |= (1ULL << s);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from the mask queued buffers for which acquire has been called,
|
||||
// since the consumer will not receive their buffer addresses and so must
|
||||
// retain their cached information
|
||||
BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
|
||||
while (current != mCore->mQueue.end()) {
|
||||
if (current->mAcquireCalled) {
|
||||
mask &= ~(1ULL << current->mSlot);
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
BQ_LOGV("getReleasedBuffers: returning mask %#" PRIx64, mask);
|
||||
*outSlotMask = mask;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
|
||||
uint32_t height) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
|
||||
"height=%u)", width, height);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mDefaultWidth = width;
|
||||
mCore->mDefaultHeight = height;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
return mCore->setDefaultMaxBufferCountLocked(bufferCount);
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::disableAsyncBuffer() {
|
||||
ATRACE_CALL();
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mConsumerListener != NULL) {
|
||||
BQ_LOGE("disableAsyncBuffer: consumer already connected");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
BQ_LOGV("disableAsyncBuffer");
|
||||
mCore->mUseAsyncBuffer = false;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
|
||||
int maxAcquiredBuffers) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (maxAcquiredBuffers < 1 ||
|
||||
maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
|
||||
BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
|
||||
maxAcquiredBuffers);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
|
||||
BQ_LOGE("setMaxAcquiredBufferCount: producer is already connected");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
|
||||
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void BufferQueueConsumer::setConsumerName(const String8& name) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("setConsumerName: '%s'", name.string());
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mConsumerName = name;
|
||||
mConsumerName = name;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mDefaultBufferFormat = defaultFormat;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("setConsumerUsageBits: %#x", usage);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mConsumerUsageBits = usage;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("setTransformHint: %#x", hint);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mTransformHint = hint;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
|
||||
return mCore->mSidebandStream;
|
||||
}
|
||||
|
||||
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
|
||||
mCore->dump(result, prefix);
|
||||
}
|
||||
|
||||
} // namespace android
|
181
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueConsumer.h
Normal file
181
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueConsumer.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2014 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_BUFFERQUEUECONSUMER_H
|
||||
#define ANDROID_GUI_BUFFERQUEUECONSUMER_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <gui/BufferQueueDefs.h>
|
||||
#include <gui/IGraphicBufferConsumer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class BufferQueueCore;
|
||||
|
||||
class BufferQueueConsumer : public BnGraphicBufferConsumer {
|
||||
|
||||
public:
|
||||
BufferQueueConsumer(const sp<BufferQueueCore>& core);
|
||||
virtual ~BufferQueueConsumer();
|
||||
|
||||
// acquireBuffer attempts to acquire ownership of the next pending buffer in
|
||||
// the BufferQueue. If no buffer is pending then it returns
|
||||
// NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
|
||||
// information about the buffer is returned in BufferItem. If the buffer
|
||||
// returned had previously been acquired then the BufferItem::mGraphicBuffer
|
||||
// field of buffer is set to NULL and it is assumed that the consumer still
|
||||
// holds a reference to the buffer.
|
||||
//
|
||||
// If expectedPresent is nonzero, it indicates the time when the buffer
|
||||
// will be displayed on screen. If the buffer's timestamp is farther in the
|
||||
// future, the buffer won't be acquired, and PRESENT_LATER will be
|
||||
// returned. The presentation time is in nanoseconds, and the time base
|
||||
// is CLOCK_MONOTONIC.
|
||||
virtual status_t acquireBuffer(BufferItem* outBuffer,
|
||||
nsecs_t expectedPresent);
|
||||
|
||||
// See IGraphicBufferConsumer::detachBuffer
|
||||
virtual status_t detachBuffer(int slot);
|
||||
|
||||
// See IGraphicBufferConsumer::attachBuffer
|
||||
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
|
||||
|
||||
// releaseBuffer releases a buffer slot from the consumer back to the
|
||||
// BufferQueue. This may be done while the buffer's contents are still
|
||||
// being accessed. The fence will signal when the buffer is no longer
|
||||
// in use. frameNumber is used to indentify the exact buffer returned.
|
||||
//
|
||||
// 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.
|
||||
virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
|
||||
const sp<Fence>& releaseFence, EGLDisplay display,
|
||||
EGLSyncKHR fence);
|
||||
|
||||
// connect connects a consumer to the BufferQueue. Only one
|
||||
// consumer may be connected, and when that consumer disconnects the
|
||||
// BufferQueue is placed into the "abandoned" state, causing most
|
||||
// interactions with the BufferQueue by the producer to fail.
|
||||
// controlledByApp indicates whether the consumer is controlled by
|
||||
// the application.
|
||||
//
|
||||
// consumerListener may not be NULL.
|
||||
virtual status_t connect(const sp<IConsumerListener>& consumerListener,
|
||||
bool controlledByApp);
|
||||
|
||||
// disconnect disconnects a consumer from the BufferQueue. All
|
||||
// buffers will be freed and the BufferQueue is placed in the "abandoned"
|
||||
// state, causing most interactions with the BufferQueue by the producer to
|
||||
// fail.
|
||||
virtual status_t disconnect();
|
||||
|
||||
// getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask
|
||||
// indicating which buffer slots have been released by the BufferQueue
|
||||
// but have not yet been released by the consumer.
|
||||
//
|
||||
// This should be called from the onBuffersReleased() callback.
|
||||
virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
|
||||
|
||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||
// dequeueBuffer when a width and height of zero is requested. Default
|
||||
// is 1x1.
|
||||
virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
|
||||
|
||||
// setDefaultMaxBufferCount sets the default value for the maximum buffer
|
||||
// count (the initial default is 2). If the producer has requested a
|
||||
// buffer count using setBufferCount, the default buffer count will only
|
||||
// take effect if the producer sets the count back to zero.
|
||||
//
|
||||
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
virtual status_t setDefaultMaxBufferCount(int bufferCount);
|
||||
|
||||
// disableAsyncBuffer disables the extra buffer used in async mode
|
||||
// (when both producer and consumer have set their "isControlledByApp"
|
||||
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
|
||||
//
|
||||
// This can only be called before connect().
|
||||
virtual status_t disableAsyncBuffer();
|
||||
|
||||
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
|
||||
// be acquired by the consumer at one time (default 1). This call will
|
||||
// fail if a producer is connected to the BufferQueue.
|
||||
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
|
||||
|
||||
// setConsumerName sets the name used in logging
|
||||
virtual void setConsumerName(const String8& name);
|
||||
|
||||
// setDefaultBufferFormat allows the BufferQueue to create
|
||||
// GraphicBuffers of a defaultFormat if no format is specified
|
||||
// in dequeueBuffer. Formats are enumerated in graphics.h; the
|
||||
// initial default is HAL_PIXEL_FORMAT_RGBA_8888.
|
||||
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
|
||||
|
||||
// setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
|
||||
// These are merged with the bits passed to dequeueBuffer. The values are
|
||||
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
|
||||
virtual status_t setConsumerUsageBits(uint32_t usage);
|
||||
|
||||
// setTransformHint bakes in rotation to buffers so overlays can be used.
|
||||
// The values are enumerated in window.h, e.g.
|
||||
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
|
||||
virtual status_t setTransformHint(uint32_t hint);
|
||||
|
||||
// Retrieve the sideband buffer stream, if any.
|
||||
virtual sp<NativeHandle> getSidebandStream() const;
|
||||
|
||||
// dump our state in a String
|
||||
virtual void dump(String8& result, const char* prefix) const;
|
||||
|
||||
// Functions required for backwards compatibility.
|
||||
// These will be modified/renamed in IGraphicBufferConsumer and will be
|
||||
// removed from this class at that time. See b/13306289.
|
||||
|
||||
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
|
||||
EGLDisplay display, EGLSyncKHR fence,
|
||||
const sp<Fence>& releaseFence) {
|
||||
return releaseBuffer(buf, frameNumber, releaseFence, display, fence);
|
||||
}
|
||||
|
||||
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
|
||||
bool controlledByApp) {
|
||||
return connect(consumer, controlledByApp);
|
||||
}
|
||||
|
||||
virtual status_t consumerDisconnect() { return disconnect(); }
|
||||
|
||||
// End functions required for backwards compatibility
|
||||
|
||||
private:
|
||||
sp<BufferQueueCore> mCore;
|
||||
|
||||
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
|
||||
BufferQueueDefs::SlotsType& mSlots;
|
||||
|
||||
// This is a cached copy of the name stored in the BufferQueueCore.
|
||||
// It's updated during setConsumerName.
|
||||
String8 mConsumerName;
|
||||
|
||||
}; // class BufferQueueConsumer
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
238
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueCore.cpp
Normal file
238
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueCore.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright 2014 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 "BufferQueueCore"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <gui/BufferItem.h>
|
||||
#include <gui/BufferQueueCore.h>
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/IProducerListener.h>
|
||||
#include <gui/ISurfaceComposer.h>
|
||||
#include <private/gui/ComposerService.h>
|
||||
|
||||
template <typename T>
|
||||
static inline T max(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
namespace android {
|
||||
|
||||
static String8 getUniqueName() {
|
||||
static volatile int32_t counter = 0;
|
||||
return String8::format("unnamed-%d-%d", getpid(),
|
||||
android_atomic_inc(&counter));
|
||||
}
|
||||
|
||||
BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
|
||||
mAllocator(allocator),
|
||||
mMutex(),
|
||||
mIsAbandoned(false),
|
||||
mConsumerControlledByApp(false),
|
||||
mConsumerName(getUniqueName()),
|
||||
mConsumerListener(),
|
||||
mConsumerUsageBits(0),
|
||||
mConnectedApi(NO_CONNECTED_API),
|
||||
mConnectedProducerListener(),
|
||||
mSlots(),
|
||||
mQueue(),
|
||||
mOverrideMaxBufferCount(0),
|
||||
mDequeueCondition(),
|
||||
mUseAsyncBuffer(true),
|
||||
mDequeueBufferCannotBlock(false),
|
||||
mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
|
||||
mDefaultWidth(1),
|
||||
mDefaultHeight(1),
|
||||
mDefaultMaxBufferCount(2),
|
||||
mMaxAcquiredBufferCount(1),
|
||||
mBufferHasBeenQueued(false),
|
||||
mFrameCounter(0),
|
||||
mTransformHint(0),
|
||||
mIsAllocating(false),
|
||||
mIsAllocatingCondition()
|
||||
{
|
||||
if (allocator == NULL) {
|
||||
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
||||
mAllocator = composer->createGraphicBufferAlloc();
|
||||
if (mAllocator == NULL) {
|
||||
BQ_LOGE("createGraphicBufferAlloc failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BufferQueueCore::~BufferQueueCore() {}
|
||||
|
||||
void BufferQueueCore::dump(String8& result, const char* prefix) const {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
String8 fifo;
|
||||
Fifo::const_iterator current(mQueue.begin());
|
||||
while (current != mQueue.end()) {
|
||||
fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], "
|
||||
"xform=0x%02x, time=%#" PRIx64 ", scale=%s\n",
|
||||
current->mSlot, current->mGraphicBuffer.get(),
|
||||
current->mCrop.left, current->mCrop.top, current->mCrop.right,
|
||||
current->mCrop.bottom, current->mTransform, current->mTimestamp,
|
||||
BufferItem::scalingModeName(current->mScalingMode));
|
||||
++current;
|
||||
}
|
||||
|
||||
result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
|
||||
"mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
|
||||
"default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n",
|
||||
prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock,
|
||||
mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint,
|
||||
mQueue.size(), fifo.string());
|
||||
|
||||
// Trim the free buffers so as to not spam the dump
|
||||
int maxBufferCount = 0;
|
||||
for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
|
||||
const BufferSlot& slot(mSlots[s]);
|
||||
if (slot.mBufferState != BufferSlot::FREE ||
|
||||
slot.mGraphicBuffer != NULL) {
|
||||
maxBufferCount = s + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int s = 0; s < maxBufferCount; ++s) {
|
||||
const BufferSlot& slot(mSlots[s]);
|
||||
const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
|
||||
result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
|
||||
(slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ",
|
||||
s, buffer.get(),
|
||||
BufferSlot::bufferStateName(slot.mBufferState));
|
||||
|
||||
if (buffer != NULL) {
|
||||
result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
|
||||
buffer->width, buffer->height, buffer->stride,
|
||||
buffer->format);
|
||||
}
|
||||
|
||||
result.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int BufferQueueCore::getMinUndequeuedBufferCountLocked(bool async) const {
|
||||
// If dequeueBuffer is allowed to error out, we don't have to add an
|
||||
// extra buffer.
|
||||
if (!mUseAsyncBuffer) {
|
||||
return mMaxAcquiredBufferCount;
|
||||
}
|
||||
|
||||
if (mDequeueBufferCannotBlock || async) {
|
||||
return mMaxAcquiredBufferCount + 1;
|
||||
}
|
||||
|
||||
return mMaxAcquiredBufferCount;
|
||||
}
|
||||
|
||||
int BufferQueueCore::getMinMaxBufferCountLocked(bool async) const {
|
||||
return getMinUndequeuedBufferCountLocked(async) + 1;
|
||||
}
|
||||
|
||||
int BufferQueueCore::getMaxBufferCountLocked(bool async) const {
|
||||
int minMaxBufferCount = getMinMaxBufferCountLocked(async);
|
||||
|
||||
int maxBufferCount = max(mDefaultMaxBufferCount, minMaxBufferCount);
|
||||
if (mOverrideMaxBufferCount != 0) {
|
||||
assert(mOverrideMaxBufferCount >= minMaxBufferCount);
|
||||
maxBufferCount = mOverrideMaxBufferCount;
|
||||
}
|
||||
|
||||
// Any buffers that are dequeued by the producer or sitting in the queue
|
||||
// waiting to be consumed need to have their slots preserved. Such buffers
|
||||
// will temporarily keep the max buffer count up until the slots no longer
|
||||
// need to be preserved.
|
||||
for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
BufferSlot::BufferState state = mSlots[s].mBufferState;
|
||||
if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) {
|
||||
maxBufferCount = s + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return maxBufferCount;
|
||||
}
|
||||
|
||||
status_t BufferQueueCore::setDefaultMaxBufferCountLocked(int count) {
|
||||
const int minBufferCount = mUseAsyncBuffer ? 2 : 1;
|
||||
if (count < minBufferCount || count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
BQ_LOGV("setDefaultMaxBufferCount: invalid count %d, should be in "
|
||||
"[%d, %d]",
|
||||
count, minBufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
BQ_LOGV("setDefaultMaxBufferCount: setting count to %d", count);
|
||||
mDefaultMaxBufferCount = count;
|
||||
mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void BufferQueueCore::freeBufferLocked(int slot) {
|
||||
BQ_LOGV("freeBufferLocked: slot %d", slot);
|
||||
mSlots[slot].mGraphicBuffer.clear();
|
||||
if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
|
||||
mSlots[slot].mNeedsCleanupOnRelease = true;
|
||||
}
|
||||
mSlots[slot].mBufferState = BufferSlot::FREE;
|
||||
mSlots[slot].mFrameNumber = UINT32_MAX;
|
||||
mSlots[slot].mAcquireCalled = false;
|
||||
|
||||
// Destroy fence as BufferQueue now takes ownership
|
||||
if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
|
||||
eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence);
|
||||
mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
|
||||
}
|
||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||
}
|
||||
|
||||
void BufferQueueCore::freeAllBuffersLocked() {
|
||||
mBufferHasBeenQueued = false;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
freeBufferLocked(s);
|
||||
}
|
||||
}
|
||||
|
||||
bool BufferQueueCore::stillTracking(const BufferItem* item) const {
|
||||
const BufferSlot& slot = mSlots[item->mSlot];
|
||||
|
||||
BQ_LOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } "
|
||||
"slot { slot=%d/%" PRIu64 " buffer=%p }",
|
||||
item->mSlot, item->mFrameNumber,
|
||||
(item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
|
||||
item->mSlot, slot.mFrameNumber,
|
||||
(slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
|
||||
|
||||
// Compare item with its original buffer slot. We can check the slot as
|
||||
// the buffer would not be moved to a different slot by the producer.
|
||||
return (slot.mGraphicBuffer != NULL) &&
|
||||
(item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
|
||||
}
|
||||
|
||||
void BufferQueueCore::waitWhileAllocatingLocked() const {
|
||||
ATRACE_CALL();
|
||||
while (mIsAllocating) {
|
||||
mIsAllocatingCondition.wait(mMutex);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
253
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueCore.h
Normal file
253
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueCore.h
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2014 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_BUFFERQUEUECORE_H
|
||||
#define ANDROID_GUI_BUFFERQUEUECORE_H
|
||||
|
||||
#include <gui/BufferQueueDefs.h>
|
||||
#include <gui/BufferSlot.h>
|
||||
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/NativeHandle.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#define BQ_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define BQ_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define BQ_LOGI(x, ...) ALOGI("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define BQ_LOGW(x, ...) ALOGW("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define BQ_LOGE(x, ...) ALOGE("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
|
||||
#define ATRACE_BUFFER_INDEX(index) \
|
||||
if (ATRACE_ENABLED()) { \
|
||||
char ___traceBuf[1024]; \
|
||||
snprintf(___traceBuf, 1024, "%s: %d", \
|
||||
mCore->mConsumerName.string(), (index)); \
|
||||
android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \
|
||||
}
|
||||
|
||||
namespace android {
|
||||
|
||||
class BufferItem;
|
||||
class IConsumerListener;
|
||||
class IGraphicBufferAlloc;
|
||||
class IProducerListener;
|
||||
|
||||
class BufferQueueCore : public virtual RefBase {
|
||||
|
||||
friend class BufferQueueProducer;
|
||||
friend class BufferQueueConsumer;
|
||||
|
||||
public:
|
||||
// Used as a placeholder slot number when the value isn't pointing to an
|
||||
// existing buffer.
|
||||
enum { INVALID_BUFFER_SLOT = -1 }; // TODO: Extract from IGBC::BufferItem
|
||||
|
||||
// We reserve two slots in order to guarantee that the producer and
|
||||
// consumer can run asynchronously.
|
||||
enum { MAX_MAX_ACQUIRED_BUFFERS = BufferQueueDefs::NUM_BUFFER_SLOTS - 2 };
|
||||
|
||||
// The default API number used to indicate that no producer is connected
|
||||
enum { NO_CONNECTED_API = 0 };
|
||||
|
||||
typedef Vector<BufferItem> Fifo;
|
||||
|
||||
// BufferQueueCore manages a pool of gralloc memory slots to be used by
|
||||
// producers and consumers. allocator is used to allocate all the needed
|
||||
// gralloc buffers.
|
||||
BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
|
||||
virtual ~BufferQueueCore();
|
||||
|
||||
private:
|
||||
// Dump our state in a string
|
||||
void dump(String8& result, const char* prefix) const;
|
||||
|
||||
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
|
||||
// that must remain in a state other than DEQUEUED. The async parameter
|
||||
// tells whether we're in asynchronous mode.
|
||||
int getMinUndequeuedBufferCountLocked(bool async) const;
|
||||
|
||||
// getMinMaxBufferCountLocked returns the minimum number of buffers allowed
|
||||
// given the current BufferQueue state. The async parameter tells whether
|
||||
// we're in asynchonous mode.
|
||||
int getMinMaxBufferCountLocked(bool async) const;
|
||||
|
||||
// getMaxBufferCountLocked returns the maximum number of buffers that can be
|
||||
// allocated at once. This value depends on the following member variables:
|
||||
//
|
||||
// mDequeueBufferCannotBlock
|
||||
// mMaxAcquiredBufferCount
|
||||
// mDefaultMaxBufferCount
|
||||
// mOverrideMaxBufferCount
|
||||
// async parameter
|
||||
//
|
||||
// Any time one of these member variables is changed while a producer is
|
||||
// connected, mDequeueCondition must be broadcast.
|
||||
int getMaxBufferCountLocked(bool async) const;
|
||||
|
||||
// setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
|
||||
// that will be used if the producer does not override the buffer slot
|
||||
// count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. The
|
||||
// initial default is 2.
|
||||
status_t setDefaultMaxBufferCountLocked(int count);
|
||||
|
||||
// freeBufferLocked frees the GraphicBuffer and sync resources for the
|
||||
// given slot.
|
||||
void freeBufferLocked(int slot);
|
||||
|
||||
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
|
||||
// all slots.
|
||||
void freeAllBuffersLocked();
|
||||
|
||||
// stillTracking returns true iff the buffer item is still being tracked
|
||||
// in one of the slots.
|
||||
bool stillTracking(const BufferItem* item) const;
|
||||
|
||||
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
|
||||
void waitWhileAllocatingLocked() const;
|
||||
|
||||
// mAllocator is the connection to SurfaceFlinger that is used to allocate
|
||||
// new GraphicBuffer objects.
|
||||
sp<IGraphicBufferAlloc> mAllocator;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of BufferQueueCore objects. It must be locked whenever any
|
||||
// member variable is accessed.
|
||||
mutable Mutex mMutex;
|
||||
|
||||
// mIsAbandoned indicates that the BufferQueue will no longer be used to
|
||||
// consume image buffers pushed to it using the IGraphicBufferProducer
|
||||
// interface. It is initialized to false, and set to true in the
|
||||
// consumerDisconnect method. A BufferQueue that is abandoned will return
|
||||
// the NO_INIT error from all IGraphicBufferProducer methods capable of
|
||||
// returning an error.
|
||||
bool mIsAbandoned;
|
||||
|
||||
// mConsumerControlledByApp indicates whether the connected consumer is
|
||||
// controlled by the application.
|
||||
bool mConsumerControlledByApp;
|
||||
|
||||
// mConsumerName is a string used to identify the BufferQueue in log
|
||||
// messages. It is set by the IGraphicBufferConsumer::setConsumerName
|
||||
// method.
|
||||
String8 mConsumerName;
|
||||
|
||||
// mConsumerListener is used to notify the connected consumer of
|
||||
// asynchronous events that it may wish to react to. It is initially
|
||||
// set to NULL and is written by consumerConnect and consumerDisconnect.
|
||||
sp<IConsumerListener> mConsumerListener;
|
||||
|
||||
// mConsumerUsageBits contains flags that the consumer wants for
|
||||
// GraphicBuffers.
|
||||
uint32_t mConsumerUsageBits;
|
||||
|
||||
// mConnectedApi indicates the producer API that is currently connected
|
||||
// to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated
|
||||
// by the connect and disconnect methods.
|
||||
int mConnectedApi;
|
||||
|
||||
// mConnectedProducerToken is used to set a binder death notification on
|
||||
// the producer.
|
||||
sp<IProducerListener> mConnectedProducerListener;
|
||||
|
||||
// mSlots is an array of buffer slots that must be mirrored on the producer
|
||||
// side. This allows buffer ownership to be transferred between the producer
|
||||
// and consumer without sending a GraphicBuffer over Binder. The entire
|
||||
// array is initialized to NULL at construction time, and buffers are
|
||||
// allocated for a slot when requestBuffer is called with that slot's index.
|
||||
BufferQueueDefs::SlotsType mSlots;
|
||||
|
||||
// mQueue is a FIFO of queued buffers used in synchronous mode.
|
||||
Fifo mQueue;
|
||||
|
||||
// mOverrideMaxBufferCount is the limit on the number of buffers that will
|
||||
// be allocated at one time. This value is set by the producer by calling
|
||||
// setBufferCount. The default is 0, which means that the producer doesn't
|
||||
// care about the number of buffers in the pool. In that case,
|
||||
// mDefaultMaxBufferCount is used as the limit.
|
||||
int mOverrideMaxBufferCount;
|
||||
|
||||
// mDequeueCondition is a condition variable used for dequeueBuffer in
|
||||
// synchronous mode.
|
||||
mutable Condition mDequeueCondition;
|
||||
|
||||
// mUseAsyncBuffer indicates whether an extra buffer is used in async mode
|
||||
// to prevent dequeueBuffer from blocking.
|
||||
bool mUseAsyncBuffer;
|
||||
|
||||
// mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
|
||||
// block. This flag is set during connect when both the producer and
|
||||
// consumer are controlled by the application.
|
||||
bool mDequeueBufferCannotBlock;
|
||||
|
||||
// mDefaultBufferFormat can be set so it will override the buffer format
|
||||
// when it isn't specified in dequeueBuffer.
|
||||
uint32_t mDefaultBufferFormat;
|
||||
|
||||
// mDefaultWidth holds the default width of allocated buffers. It is used
|
||||
// in dequeueBuffer if a width and height of 0 are specified.
|
||||
int mDefaultWidth;
|
||||
|
||||
// mDefaultHeight holds the default height of allocated buffers. It is used
|
||||
// in dequeueBuffer if a width and height of 0 are specified.
|
||||
int mDefaultHeight;
|
||||
|
||||
// mDefaultMaxBufferCount is the default limit on the number of buffers that
|
||||
// will be allocated at one time. This default limit is set by the consumer.
|
||||
// The limit (as opposed to the default limit) may be overriden by the
|
||||
// producer.
|
||||
int mDefaultMaxBufferCount;
|
||||
|
||||
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
|
||||
// acquire at one time. It defaults to 1, and can be changed by the consumer
|
||||
// via setMaxAcquiredBufferCount, but this may only be done while no
|
||||
// producer is connected to the BufferQueue. This value is used to derive
|
||||
// the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer.
|
||||
int mMaxAcquiredBufferCount;
|
||||
|
||||
// mBufferHasBeenQueued is true once a buffer has been queued. It is reset
|
||||
// when something causes all buffers to be freed (e.g., changing the buffer
|
||||
// count).
|
||||
bool mBufferHasBeenQueued;
|
||||
|
||||
// mFrameCounter is the free running counter, incremented on every
|
||||
// successful queueBuffer call and buffer allocation.
|
||||
uint64_t mFrameCounter;
|
||||
|
||||
// mTransformHint is used to optimize for screen rotations.
|
||||
uint32_t mTransformHint;
|
||||
|
||||
// mSidebandStream is a handle to the sideband buffer stream, if any
|
||||
sp<NativeHandle> mSidebandStream;
|
||||
|
||||
// mIsAllocating indicates whether a producer is currently trying to allocate buffers (which
|
||||
// releases mMutex while doing the allocation proper). Producers should not modify any of the
|
||||
// FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to
|
||||
// false.
|
||||
bool mIsAllocating;
|
||||
|
||||
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
|
||||
// becomes false.
|
||||
mutable Condition mIsAllocatingCondition;
|
||||
}; // class BufferQueueCore
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
35
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueDefs.h
Normal file
35
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueDefs.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2014 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_BUFFERQUEUECOREDEFS_H
|
||||
#define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
|
||||
|
||||
#include <gui/BufferSlot.h>
|
||||
|
||||
namespace android {
|
||||
class BufferQueueCore;
|
||||
|
||||
namespace BufferQueueDefs {
|
||||
// BufferQueue will keep track of at most this value of buffers.
|
||||
// Attempts at runtime to increase the number of buffers past this
|
||||
// will fail.
|
||||
enum { NUM_BUFFER_SLOTS = 64 };
|
||||
|
||||
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
|
||||
} // namespace BufferQueueDefs
|
||||
} // namespace android
|
||||
|
||||
#endif
|
@ -0,0 +1,982 @@
|
||||
/*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOG_TAG "BufferQueueProducer"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
|
||||
#include <gui/BufferItem.h>
|
||||
#include <gui/BufferQueueCore.h>
|
||||
#include <gui/BufferQueueProducer.h>
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/IProducerListener.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
|
||||
mCore(core),
|
||||
mSlots(core->mSlots),
|
||||
mConsumerName(),
|
||||
mStickyTransform(0) {}
|
||||
|
||||
BufferQueueProducer::~BufferQueueProducer() {}
|
||||
|
||||
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("requestBuffer: slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
|
||||
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
|
||||
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mSlots[slot].mRequestBufferCalled = true;
|
||||
*buf = mSlots[slot].mGraphicBuffer;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::setBufferCount(int bufferCount) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("setBufferCount: count = %d", bufferCount);
|
||||
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)",
|
||||
bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// There must be no dequeued buffers when changing the buffer count.
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
|
||||
BQ_LOGE("setBufferCount: buffer owned by producer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferCount == 0) {
|
||||
mCore->mOverrideMaxBufferCount = 0;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false);
|
||||
if (bufferCount < minBufferSlots) {
|
||||
BQ_LOGE("setBufferCount: requested buffer count %d is less than "
|
||||
"minimum %d", bufferCount, minBufferSlots);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Here we are guaranteed that the producer doesn't have any dequeued
|
||||
// buffers and will release all of its buffer references. We don't
|
||||
// clear the queue, however, so that currently queued buffers still
|
||||
// get displayed.
|
||||
mCore->freeAllBuffersLocked();
|
||||
mCore->mOverrideMaxBufferCount = bufferCount;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
listener = mCore->mConsumerListener;
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onBuffersReleased();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
|
||||
bool async, int* found, status_t* returnFlags) const {
|
||||
bool tryAgain = true;
|
||||
while (tryAgain) {
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("%s: BufferQueue has been abandoned", caller);
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
||||
if (async && mCore->mOverrideMaxBufferCount) {
|
||||
// FIXME: Some drivers are manually setting the buffer count
|
||||
// (which they shouldn't), so we do this extra test here to
|
||||
// handle that case. This is TEMPORARY until we get this fixed.
|
||||
if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
|
||||
BQ_LOGE("%s: async mode is invalid with buffer count override",
|
||||
caller);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Free up any buffers that are in slots beyond the max buffer count
|
||||
for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
assert(mSlots[s].mBufferState == BufferSlot::FREE);
|
||||
if (mSlots[s].mGraphicBuffer != NULL) {
|
||||
mCore->freeBufferLocked(s);
|
||||
*returnFlags |= RELEASE_ALL_BUFFERS;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for a free buffer to give to the client
|
||||
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
int dequeuedCount = 0;
|
||||
int acquiredCount = 0;
|
||||
for (int s = 0; s < maxBufferCount; ++s) {
|
||||
switch (mSlots[s].mBufferState) {
|
||||
case BufferSlot::DEQUEUED:
|
||||
++dequeuedCount;
|
||||
break;
|
||||
case BufferSlot::ACQUIRED:
|
||||
++acquiredCount;
|
||||
break;
|
||||
case BufferSlot::FREE:
|
||||
// We return the oldest of the free buffers to avoid
|
||||
// stalling the producer if possible, since the consumer
|
||||
// may still have pending reads of in-flight buffers
|
||||
if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
|
||||
mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
|
||||
*found = s;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Producers are not allowed to dequeue more than one buffer if they
|
||||
// did not set a buffer count
|
||||
if (!mCore->mOverrideMaxBufferCount && dequeuedCount) {
|
||||
BQ_LOGE("%s: can't dequeue multiple buffers without setting the "
|
||||
"buffer count", caller);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// See whether a buffer has been queued since the last
|
||||
// setBufferCount so we know whether to perform the min undequeued
|
||||
// buffers check below
|
||||
if (mCore->mBufferHasBeenQueued) {
|
||||
// Make sure the producer is not trying to dequeue more buffers
|
||||
// than allowed
|
||||
const int newUndequeuedCount =
|
||||
maxBufferCount - (dequeuedCount + 1);
|
||||
const int minUndequeuedCount =
|
||||
mCore->getMinUndequeuedBufferCountLocked(async);
|
||||
if (newUndequeuedCount < minUndequeuedCount) {
|
||||
BQ_LOGE("%s: min undequeued buffer count (%d) exceeded "
|
||||
"(dequeued=%d undequeued=%d)",
|
||||
caller, minUndequeuedCount,
|
||||
dequeuedCount, newUndequeuedCount);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
}
|
||||
|
||||
// If we disconnect and reconnect quickly, we can be in a state where
|
||||
// our slots are empty but we have many buffers in the queue. This can
|
||||
// cause us to run out of memory if we outrun the consumer. Wait here if
|
||||
// it looks like we have too many buffers queued up.
|
||||
bool tooManyBuffers = mCore->mQueue.size()
|
||||
> static_cast<size_t>(maxBufferCount);
|
||||
if (tooManyBuffers) {
|
||||
BQ_LOGV("%s: queue size is %zu, waiting", caller,
|
||||
mCore->mQueue.size());
|
||||
}
|
||||
|
||||
// If no buffer is found, or if the queue has too many buffers
|
||||
// outstanding, wait for a buffer to be acquired or released, or for the
|
||||
// max buffer count to change.
|
||||
tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
|
||||
tooManyBuffers;
|
||||
if (tryAgain) {
|
||||
// Return an error if we're in non-blocking mode (producer and
|
||||
// consumer are controlled by the application).
|
||||
// However, the consumer is allowed to briefly acquire an extra
|
||||
// buffer (which could cause us to have to wait here), which is
|
||||
// okay, since it is only used to implement an atomic acquire +
|
||||
// release (e.g., in GLConsumer::updateTexImage())
|
||||
if (mCore->mDequeueBufferCannotBlock &&
|
||||
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
mCore->mDequeueCondition.wait(mCore->mMutex);
|
||||
}
|
||||
} // while (tryAgain)
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
|
||||
sp<android::Fence> *outFence, bool async,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
|
||||
ATRACE_CALL();
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mConsumerName = mCore->mConsumerName;
|
||||
} // Autolock scope
|
||||
|
||||
BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x",
|
||||
async ? "true" : "false", width, height, format, usage);
|
||||
|
||||
if ((width && !height) || (!width && height)) {
|
||||
BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t returnFlags = NO_ERROR;
|
||||
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
|
||||
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
|
||||
bool attachedByConsumer = false;
|
||||
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (format == 0) {
|
||||
format = mCore->mDefaultBufferFormat;
|
||||
}
|
||||
|
||||
// Enable the usage bits the consumer requested
|
||||
usage |= mCore->mConsumerUsageBits;
|
||||
|
||||
int found;
|
||||
status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
|
||||
&found, &returnFlags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
BQ_LOGE("dequeueBuffer: no available buffer slots");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
ATRACE_BUFFER_INDEX(found);
|
||||
|
||||
attachedByConsumer = mSlots[found].mAttachedByConsumer;
|
||||
|
||||
const bool useDefaultSize = !width && !height;
|
||||
if (useDefaultSize) {
|
||||
width = mCore->mDefaultWidth;
|
||||
height = mCore->mDefaultHeight;
|
||||
}
|
||||
|
||||
mSlots[found].mBufferState = BufferSlot::DEQUEUED;
|
||||
|
||||
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
|
||||
if ((buffer == NULL) ||
|
||||
(static_cast<uint32_t>(buffer->width) != width) ||
|
||||
(static_cast<uint32_t>(buffer->height) != height) ||
|
||||
(static_cast<uint32_t>(buffer->format) != format) ||
|
||||
((static_cast<uint32_t>(buffer->usage) & usage) != usage))
|
||||
{
|
||||
mSlots[found].mAcquireCalled = false;
|
||||
mSlots[found].mGraphicBuffer = NULL;
|
||||
mSlots[found].mRequestBufferCalled = false;
|
||||
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
|
||||
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
|
||||
mSlots[found].mFence = Fence::NO_FENCE;
|
||||
|
||||
returnFlags |= BUFFER_NEEDS_REALLOCATION;
|
||||
}
|
||||
|
||||
if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
|
||||
BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
|
||||
"slot=%d w=%d h=%d format=%u",
|
||||
found, buffer->width, buffer->height, buffer->format);
|
||||
}
|
||||
|
||||
eglDisplay = mSlots[found].mEglDisplay;
|
||||
eglFence = mSlots[found].mEglFence;
|
||||
*outFence = mSlots[found].mFence;
|
||||
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
|
||||
mSlots[found].mFence = Fence::NO_FENCE;
|
||||
} // Autolock scope
|
||||
|
||||
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
|
||||
status_t error;
|
||||
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
|
||||
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
|
||||
width, height, format, usage, &error));
|
||||
if (graphicBuffer == NULL) {
|
||||
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
mSlots[*outSlot].mFrameNumber = UINT32_MAX;
|
||||
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
|
||||
} // Autolock scope
|
||||
}
|
||||
|
||||
if (attachedByConsumer) {
|
||||
returnFlags |= BUFFER_NEEDS_REALLOCATION;
|
||||
}
|
||||
|
||||
if (eglFence != EGL_NO_SYNC_KHR) {
|
||||
EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
|
||||
1000000000);
|
||||
// If something goes wrong, log the error, but return the buffer without
|
||||
// synchronizing access to it. It's too late at this point to abort the
|
||||
// dequeue operation.
|
||||
if (result == EGL_FALSE) {
|
||||
BQ_LOGE("dequeueBuffer: error %#x waiting for fence",
|
||||
eglGetError());
|
||||
} else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
|
||||
BQ_LOGE("dequeueBuffer: timeout waiting for fence");
|
||||
}
|
||||
eglDestroySyncKHR(eglDisplay, eglFence);
|
||||
}
|
||||
|
||||
BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
|
||||
*outSlot,
|
||||
mSlots[*outSlot].mFrameNumber,
|
||||
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
|
||||
|
||||
return returnFlags;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::detachBuffer(int slot) {
|
||||
ATRACE_CALL();
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
BQ_LOGV("detachBuffer(P): slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)",
|
||||
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
|
||||
BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
} else if (!mSlots[slot].mRequestBufferCalled) {
|
||||
BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested",
|
||||
slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mCore->freeBufferLocked(slot);
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
|
||||
sp<Fence>* outFence) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outBuffer == NULL) {
|
||||
BQ_LOGE("detachNextBuffer: outBuffer must not be NULL");
|
||||
return BAD_VALUE;
|
||||
} else if (outFence == NULL) {
|
||||
BQ_LOGE("detachNextBuffer: outFence must not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
// Find the oldest valid slot
|
||||
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (mSlots[s].mBufferState == BufferSlot::FREE &&
|
||||
mSlots[s].mGraphicBuffer != NULL) {
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
|
||||
mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
|
||||
found = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
BQ_LOGV("detachNextBuffer detached slot %d", found);
|
||||
|
||||
*outBuffer = mSlots[found].mGraphicBuffer;
|
||||
*outFence = mSlots[found].mFence;
|
||||
mCore->freeBufferLocked(found);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::attachBuffer(int* outSlot,
|
||||
const sp<android::GraphicBuffer>& buffer) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (outSlot == NULL) {
|
||||
BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
|
||||
return BAD_VALUE;
|
||||
} else if (buffer == NULL) {
|
||||
BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
status_t returnFlags = NO_ERROR;
|
||||
int found;
|
||||
// TODO: Should we provide an async flag to attachBuffer? It seems
|
||||
// unlikely that buffers which we are attaching to a BufferQueue will
|
||||
// be asynchronous (droppable), but it may not be impossible.
|
||||
status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false,
|
||||
&found, &returnFlags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
|
||||
BQ_LOGE("attachBuffer(P): no available buffer slots");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
*outSlot = found;
|
||||
ATRACE_BUFFER_INDEX(*outSlot);
|
||||
BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x",
|
||||
*outSlot, returnFlags);
|
||||
|
||||
mSlots[*outSlot].mGraphicBuffer = buffer;
|
||||
mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED;
|
||||
mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
|
||||
mSlots[*outSlot].mFence = Fence::NO_FENCE;
|
||||
mSlots[*outSlot].mRequestBufferCalled = true;
|
||||
|
||||
return returnFlags;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::queueBuffer(int slot,
|
||||
const QueueBufferInput &input, QueueBufferOutput *output) {
|
||||
ATRACE_CALL();
|
||||
ATRACE_BUFFER_INDEX(slot);
|
||||
|
||||
int64_t timestamp;
|
||||
bool isAutoTimestamp;
|
||||
Rect crop;
|
||||
int scalingMode;
|
||||
uint32_t transform;
|
||||
uint32_t stickyTransform;
|
||||
bool async;
|
||||
sp<Fence> fence;
|
||||
input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
|
||||
&async, &fence, &stickyTransform);
|
||||
|
||||
if (fence == NULL) {
|
||||
BQ_LOGE("queueBuffer: fence is NULL");
|
||||
// Temporary workaround for b/17946343: soldier-on instead of returning an error. This
|
||||
// prevents the client from dying, at the risk of visible corruption due to hwcomposer
|
||||
// reading the buffer before the producer is done rendering it. Unless the buffer is the
|
||||
// last frame of an animation, the corruption will be transient.
|
||||
fence = Fence::NO_FENCE;
|
||||
// return BAD_VALUE;
|
||||
}
|
||||
|
||||
switch (scalingMode) {
|
||||
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
|
||||
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
|
||||
case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
|
||||
break;
|
||||
default:
|
||||
BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
||||
if (async && mCore->mOverrideMaxBufferCount) {
|
||||
// FIXME: Some drivers are manually setting the buffer count
|
||||
// (which they shouldn't), so we do this extra test here to
|
||||
// handle that case. This is TEMPORARY until we get this fixed.
|
||||
if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
|
||||
BQ_LOGE("queueBuffer: async mode is invalid with "
|
||||
"buffer count override");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= maxBufferCount) {
|
||||
BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
|
||||
slot, maxBufferCount);
|
||||
return BAD_VALUE;
|
||||
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
|
||||
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return BAD_VALUE;
|
||||
} else if (!mSlots[slot].mRequestBufferCalled) {
|
||||
BQ_LOGE("queueBuffer: slot %d was queued without requesting "
|
||||
"a buffer", slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64
|
||||
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
|
||||
slot, mCore->mFrameCounter + 1, timestamp,
|
||||
crop.left, crop.top, crop.right, crop.bottom,
|
||||
transform, BufferItem::scalingModeName(scalingMode));
|
||||
|
||||
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
|
||||
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
|
||||
Rect croppedRect;
|
||||
crop.intersect(bufferRect, &croppedRect);
|
||||
if (croppedRect != crop) {
|
||||
BQ_LOGE("queueBuffer: crop rect is not contained within the "
|
||||
"buffer in slot %d", slot);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mSlots[slot].mFence = fence;
|
||||
mSlots[slot].mBufferState = BufferSlot::QUEUED;
|
||||
++mCore->mFrameCounter;
|
||||
mSlots[slot].mFrameNumber = mCore->mFrameCounter;
|
||||
|
||||
BufferItem item;
|
||||
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
|
||||
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
|
||||
item.mCrop = crop;
|
||||
item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
|
||||
item.mTransformToDisplayInverse =
|
||||
bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
|
||||
item.mScalingMode = scalingMode;
|
||||
item.mTimestamp = timestamp;
|
||||
item.mIsAutoTimestamp = isAutoTimestamp;
|
||||
item.mFrameNumber = mCore->mFrameCounter;
|
||||
item.mSlot = slot;
|
||||
item.mFence = fence;
|
||||
item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
|
||||
|
||||
mStickyTransform = stickyTransform;
|
||||
|
||||
if (mCore->mQueue.empty()) {
|
||||
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
|
||||
// and simply queue this buffer
|
||||
mCore->mQueue.push_back(item);
|
||||
listener = mCore->mConsumerListener;
|
||||
} else {
|
||||
// When the queue is not empty, we need to look at the front buffer
|
||||
// state to see if we need to replace it
|
||||
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
|
||||
if (front->mIsDroppable) {
|
||||
// If the front queued buffer is still being tracked, we first
|
||||
// mark it as freed
|
||||
if (mCore->stillTracking(front)) {
|
||||
mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
|
||||
// Reset the frame number of the freed buffer so that it is
|
||||
// the first in line to be dequeued again
|
||||
mSlots[front->mSlot].mFrameNumber = 0;
|
||||
}
|
||||
// Overwrite the droppable buffer with the incoming one
|
||||
*front = item;
|
||||
} else {
|
||||
mCore->mQueue.push_back(item);
|
||||
listener = mCore->mConsumerListener;
|
||||
}
|
||||
}
|
||||
|
||||
mCore->mBufferHasBeenQueued = true;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
|
||||
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
|
||||
mCore->mTransformHint, mCore->mQueue.size());
|
||||
|
||||
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onFrameAvailable();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("cancelBuffer: slot %d", slot);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
|
||||
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
|
||||
return;
|
||||
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
|
||||
BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
|
||||
"(state = %d)", slot, mSlots[slot].mBufferState);
|
||||
return;
|
||||
} else if (fence == NULL) {
|
||||
BQ_LOGE("cancelBuffer: fence is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
mSlots[slot].mBufferState = BufferSlot::FREE;
|
||||
mSlots[slot].mFrameNumber = 0;
|
||||
mSlots[slot].mFence = fence;
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
}
|
||||
|
||||
int BufferQueueProducer::query(int what, int *outValue) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
|
||||
if (outValue == NULL) {
|
||||
BQ_LOGE("query: outValue was NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("query: BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
int value;
|
||||
switch (what) {
|
||||
case NATIVE_WINDOW_WIDTH:
|
||||
value = mCore->mDefaultWidth;
|
||||
break;
|
||||
case NATIVE_WINDOW_HEIGHT:
|
||||
value = mCore->mDefaultHeight;
|
||||
break;
|
||||
case NATIVE_WINDOW_FORMAT:
|
||||
value = mCore->mDefaultBufferFormat;
|
||||
break;
|
||||
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
|
||||
value = mCore->getMinUndequeuedBufferCountLocked(false);
|
||||
break;
|
||||
case NATIVE_WINDOW_STICKY_TRANSFORM:
|
||||
value = static_cast<int>(mStickyTransform);
|
||||
break;
|
||||
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
|
||||
value = (mCore->mQueue.size() > 1);
|
||||
break;
|
||||
case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
|
||||
value = mCore->mConsumerUsageBits;
|
||||
break;
|
||||
default:
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
BQ_LOGV("query: %d? %d", what, value);
|
||||
*outValue = value;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput *output) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mConsumerName = mCore->mConsumerName;
|
||||
BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api,
|
||||
producerControlledByApp ? "true" : "false");
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
BQ_LOGE("connect(P): BufferQueue has been abandoned");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (mCore->mConsumerListener == NULL) {
|
||||
BQ_LOGE("connect(P): BufferQueue has no consumer");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (output == NULL) {
|
||||
BQ_LOGE("connect(P): output was NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
|
||||
BQ_LOGE("connect(P): already connected (cur=%d req=%d)",
|
||||
mCore->mConnectedApi, api);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int status = NO_ERROR;
|
||||
switch (api) {
|
||||
case NATIVE_WINDOW_API_EGL:
|
||||
case NATIVE_WINDOW_API_CPU:
|
||||
case NATIVE_WINDOW_API_MEDIA:
|
||||
case NATIVE_WINDOW_API_CAMERA:
|
||||
mCore->mConnectedApi = api;
|
||||
output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
|
||||
mCore->mTransformHint, mCore->mQueue.size());
|
||||
|
||||
// Set up a death notification so that we can disconnect
|
||||
// automatically if the remote producer dies
|
||||
if (listener != NULL &&
|
||||
listener->asBinder()->remoteBinder() != NULL) {
|
||||
status = listener->asBinder()->linkToDeath(
|
||||
static_cast<IBinder::DeathRecipient*>(this));
|
||||
if (status != NO_ERROR) {
|
||||
BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
|
||||
strerror(-status), status);
|
||||
}
|
||||
}
|
||||
mCore->mConnectedProducerListener = listener;
|
||||
break;
|
||||
default:
|
||||
BQ_LOGE("connect(P): unknown API %d", api);
|
||||
status = BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
mCore->mBufferHasBeenQueued = false;
|
||||
mCore->mDequeueBufferCannotBlock =
|
||||
mCore->mConsumerControlledByApp && producerControlledByApp;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::disconnect(int api) {
|
||||
ATRACE_CALL();
|
||||
BQ_LOGV("disconnect(P): api %d", api);
|
||||
|
||||
int status = NO_ERROR;
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
if (mCore->mIsAbandoned) {
|
||||
// It's not really an error to disconnect after the surface has
|
||||
// been abandoned; it should just be a no-op.
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
switch (api) {
|
||||
case NATIVE_WINDOW_API_EGL:
|
||||
case NATIVE_WINDOW_API_CPU:
|
||||
case NATIVE_WINDOW_API_MEDIA:
|
||||
case NATIVE_WINDOW_API_CAMERA:
|
||||
if (mCore->mConnectedApi == api) {
|
||||
mCore->freeAllBuffersLocked();
|
||||
|
||||
// Remove our death notification callback if we have one
|
||||
if (mCore->mConnectedProducerListener != NULL) {
|
||||
sp<IBinder> token =
|
||||
mCore->mConnectedProducerListener->asBinder();
|
||||
// This can fail if we're here because of the death
|
||||
// notification, but we just ignore it
|
||||
token->unlinkToDeath(
|
||||
static_cast<IBinder::DeathRecipient*>(this));
|
||||
}
|
||||
mCore->mConnectedProducerListener = NULL;
|
||||
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
|
||||
mCore->mSidebandStream.clear();
|
||||
mCore->mDequeueCondition.broadcast();
|
||||
listener = mCore->mConsumerListener;
|
||||
} else {
|
||||
BQ_LOGE("disconnect(P): connected to another API "
|
||||
"(cur=%d req=%d)", mCore->mConnectedApi, api);
|
||||
status = BAD_VALUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BQ_LOGE("disconnect(P): unknown API %d", api);
|
||||
status = BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
} // Autolock scope
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != NULL) {
|
||||
listener->onBuffersReleased();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
|
||||
sp<IConsumerListener> listener;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock _l(mCore->mMutex);
|
||||
mCore->mSidebandStream = stream;
|
||||
listener = mCore->mConsumerListener;
|
||||
} // Autolock scope
|
||||
|
||||
if (listener != NULL) {
|
||||
listener->onSidebandStreamChanged();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
|
||||
uint32_t height, uint32_t format, uint32_t usage) {
|
||||
ATRACE_CALL();
|
||||
while (true) {
|
||||
Vector<int> freeSlots;
|
||||
size_t newBufferCount = 0;
|
||||
uint32_t allocWidth = 0;
|
||||
uint32_t allocHeight = 0;
|
||||
uint32_t allocFormat = 0;
|
||||
uint32_t allocUsage = 0;
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->waitWhileAllocatingLocked();
|
||||
|
||||
int currentBufferCount = 0;
|
||||
for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||
if (mSlots[slot].mGraphicBuffer != NULL) {
|
||||
++currentBufferCount;
|
||||
} else {
|
||||
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
|
||||
BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
|
||||
slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
freeSlots.push_back(slot);
|
||||
}
|
||||
}
|
||||
|
||||
int maxBufferCount = mCore->getMaxBufferCountLocked(async);
|
||||
BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
|
||||
currentBufferCount, maxBufferCount);
|
||||
if (maxBufferCount <= currentBufferCount)
|
||||
return;
|
||||
newBufferCount = maxBufferCount - currentBufferCount;
|
||||
if (freeSlots.size() < newBufferCount) {
|
||||
BQ_LOGE("allocateBuffers: ran out of free slots");
|
||||
return;
|
||||
}
|
||||
allocWidth = width > 0 ? width : mCore->mDefaultWidth;
|
||||
allocHeight = height > 0 ? height : mCore->mDefaultHeight;
|
||||
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
|
||||
allocUsage = usage | mCore->mConsumerUsageBits;
|
||||
|
||||
mCore->mIsAllocating = true;
|
||||
} // Autolock scope
|
||||
|
||||
Vector<sp<GraphicBuffer> > buffers;
|
||||
for (size_t i = 0; i < newBufferCount; ++i) {
|
||||
status_t result = NO_ERROR;
|
||||
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
|
||||
allocWidth, allocHeight, allocFormat, allocUsage, &result));
|
||||
if (result != NO_ERROR) {
|
||||
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
|
||||
" %u, usage %u)", width, height, format, usage);
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
mCore->mIsAllocating = false;
|
||||
mCore->mIsAllocatingCondition.broadcast();
|
||||
return;
|
||||
}
|
||||
buffers.push_back(graphicBuffer);
|
||||
}
|
||||
|
||||
{ // Autolock scope
|
||||
Mutex::Autolock lock(mCore->mMutex);
|
||||
uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
|
||||
uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
|
||||
uint32_t checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
|
||||
uint32_t checkUsage = usage | mCore->mConsumerUsageBits;
|
||||
if (checkWidth != allocWidth || checkHeight != allocHeight ||
|
||||
checkFormat != allocFormat || checkUsage != allocUsage) {
|
||||
// Something changed while we released the lock. Retry.
|
||||
BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
|
||||
mCore->mIsAllocating = false;
|
||||
mCore->mIsAllocatingCondition.broadcast();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < newBufferCount; ++i) {
|
||||
int slot = freeSlots[i];
|
||||
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
|
||||
// A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
|
||||
// allocated.
|
||||
BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
|
||||
"Dropping allocated buffer.", slot);
|
||||
continue;
|
||||
}
|
||||
mCore->freeBufferLocked(slot); // Clean up the slot first
|
||||
mSlots[slot].mGraphicBuffer = buffers[i];
|
||||
mSlots[slot].mFrameNumber = 0;
|
||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||
BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
|
||||
}
|
||||
|
||||
mCore->mIsAllocating = false;
|
||||
mCore->mIsAllocatingCondition.broadcast();
|
||||
} // Autolock scope
|
||||
}
|
||||
}
|
||||
|
||||
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
|
||||
// If we're here, it means that a producer we were connected to died.
|
||||
// We're guaranteed that we are still connected to it because we remove
|
||||
// this callback upon disconnect. It's therefore safe to read mConnectedApi
|
||||
// without synchronization here.
|
||||
int api = mCore->mConnectedApi;
|
||||
disconnect(api);
|
||||
}
|
||||
|
||||
} // namespace android
|
204
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueProducer.h
Normal file
204
widget/gonk/nativewindow/GonkBufferQueueLL/BufferQueueProducer.h
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2014 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_BUFFERQUEUEPRODUCER_H
|
||||
#define ANDROID_GUI_BUFFERQUEUEPRODUCER_H
|
||||
|
||||
#include <gui/BufferQueueDefs.h>
|
||||
#include <gui/IGraphicBufferProducer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class BufferSlot;
|
||||
|
||||
class BufferQueueProducer : public BnGraphicBufferProducer,
|
||||
private IBinder::DeathRecipient {
|
||||
public:
|
||||
friend class BufferQueue; // Needed to access binderDied
|
||||
|
||||
BufferQueueProducer(const sp<BufferQueueCore>& core);
|
||||
virtual ~BufferQueueProducer();
|
||||
|
||||
// requestBuffer returns the GraphicBuffer for slot N.
|
||||
//
|
||||
// In normal operation, this is called the first time slot N is returned
|
||||
// by dequeueBuffer. It must be called again if dequeueBuffer returns
|
||||
// flags indicating that previously-returned buffers are no longer valid.
|
||||
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
|
||||
|
||||
// setBufferCount updates the number of available buffer slots. If this
|
||||
// method succeeds, buffer slots will be both unallocated and owned by
|
||||
// the BufferQueue object (i.e. they are not owned by the producer or
|
||||
// consumer).
|
||||
//
|
||||
// This will fail if the producer has dequeued any buffers, or if
|
||||
// bufferCount is invalid. bufferCount must generally be a value
|
||||
// between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS
|
||||
// (inclusive). It may also be set to zero (the default) to indicate
|
||||
// that the producer does not wish to set a value. The minimum value
|
||||
// can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
|
||||
// ...).
|
||||
//
|
||||
// This may only be called by the producer. The consumer will be told
|
||||
// to discard buffers through the onBuffersReleased callback.
|
||||
virtual status_t setBufferCount(int bufferCount);
|
||||
|
||||
// dequeueBuffer gets the next buffer slot index for the producer to use.
|
||||
// If a buffer slot is available then that slot index is written to the
|
||||
// location pointed to by the buf argument and a status of OK is returned.
|
||||
// If no slot is available then a status of -EBUSY is returned and buf is
|
||||
// unmodified.
|
||||
//
|
||||
// The outFence parameter will be updated to hold the fence associated with
|
||||
// the buffer. The contents of the buffer must not be overwritten until the
|
||||
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
|
||||
// written immediately.
|
||||
//
|
||||
// The width and height parameters must be no greater than the minimum of
|
||||
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
|
||||
// An error due to invalid dimensions might not be reported until
|
||||
// updateTexImage() is called. If width and height are both zero, the
|
||||
// default values specified by setDefaultBufferSize() are used instead.
|
||||
//
|
||||
// The pixel formats are enumerated in graphics.h, e.g.
|
||||
// HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
|
||||
// will be used.
|
||||
//
|
||||
// The usage argument specifies gralloc buffer usage flags. The values
|
||||
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
|
||||
// will be merged with the usage flags specified by setConsumerUsageBits.
|
||||
//
|
||||
// The return value may be a negative error value or a non-negative
|
||||
// collection of flags. If the flags are set, the return values are
|
||||
// valid, but additional actions must be performed.
|
||||
//
|
||||
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
|
||||
// producer must discard cached GraphicBuffer references for the slot
|
||||
// returned in buf.
|
||||
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
|
||||
// must discard cached GraphicBuffer references for all slots.
|
||||
//
|
||||
// In both cases, the producer will need to call requestBuffer to get a
|
||||
// GraphicBuffer handle for the returned slot.
|
||||
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
|
||||
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
|
||||
|
||||
// See IGraphicBufferProducer::detachBuffer
|
||||
virtual status_t detachBuffer(int slot);
|
||||
|
||||
// See IGraphicBufferProducer::detachNextBuffer
|
||||
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
|
||||
sp<Fence>* outFence);
|
||||
|
||||
// See IGraphicBufferProducer::attachBuffer
|
||||
virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);
|
||||
|
||||
// queueBuffer returns a filled buffer to the BufferQueue.
|
||||
//
|
||||
// Additional data is provided in the QueueBufferInput struct. Notably,
|
||||
// a timestamp must be provided for the buffer. The timestamp is in
|
||||
// nanoseconds, and must be monotonically increasing. Its other semantics
|
||||
// (zero point, etc) are producer-specific and should be documented by the
|
||||
// producer.
|
||||
//
|
||||
// The caller may provide a fence that signals when all rendering
|
||||
// operations have completed. Alternatively, NO_FENCE may be used,
|
||||
// indicating that the buffer is ready immediately.
|
||||
//
|
||||
// Some values are returned in the output struct: the current settings
|
||||
// for default width and height, the current transform hint, and the
|
||||
// number of queued buffers.
|
||||
virtual status_t queueBuffer(int slot,
|
||||
const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
|
||||
// cancelBuffer returns a dequeued buffer to the BufferQueue, but doesn't
|
||||
// queue it for use by the consumer.
|
||||
//
|
||||
// The buffer will not be overwritten until the fence signals. The fence
|
||||
// will usually be the one obtained from dequeueBuffer.
|
||||
virtual void cancelBuffer(int slot, const sp<Fence>& fence);
|
||||
|
||||
// Query native window attributes. The "what" values are enumerated in
|
||||
// window.h (e.g. NATIVE_WINDOW_FORMAT).
|
||||
virtual int query(int what, int* outValue);
|
||||
|
||||
// connect attempts to connect a producer API to the BufferQueue. This
|
||||
// must be called before any other IGraphicBufferProducer methods are
|
||||
// called except for getAllocator. A consumer must already be connected.
|
||||
//
|
||||
// This method will fail if connect was previously called on the
|
||||
// BufferQueue and no corresponding disconnect call was made (i.e. if
|
||||
// it's still connected to a producer).
|
||||
//
|
||||
// APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
|
||||
virtual status_t connect(const sp<IProducerListener>& listener,
|
||||
int api, bool producerControlledByApp, QueueBufferOutput* output);
|
||||
|
||||
// disconnect attempts to disconnect a producer API from the BufferQueue.
|
||||
// Calling this method will cause any subsequent calls to other
|
||||
// IGraphicBufferProducer methods to fail except for getAllocator and connect.
|
||||
// Successfully calling connect after this will allow the other methods to
|
||||
// succeed again.
|
||||
//
|
||||
// This method will fail if the the BufferQueue is not currently
|
||||
// connected to the specified producer API.
|
||||
virtual status_t disconnect(int api);
|
||||
|
||||
// Attaches a sideband buffer stream to the IGraphicBufferProducer.
|
||||
//
|
||||
// A sideband stream is a device-specific mechanism for passing buffers
|
||||
// from the producer to the consumer without using dequeueBuffer/
|
||||
// queueBuffer. If a sideband stream is present, the consumer can choose
|
||||
// whether to acquire buffers from the sideband stream or from the queued
|
||||
// buffers.
|
||||
//
|
||||
// Passing NULL or a different stream handle will detach the previous
|
||||
// handle if any.
|
||||
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
|
||||
|
||||
// See IGraphicBufferProducer::allocateBuffers
|
||||
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t usage);
|
||||
|
||||
private:
|
||||
// This is required by the IBinder::DeathRecipient interface
|
||||
virtual void binderDied(const wp<IBinder>& who);
|
||||
|
||||
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
|
||||
// block if there are no available slots and we are not in non-blocking
|
||||
// mode (producer and consumer controlled by the application). If it blocks,
|
||||
// it will release mCore->mMutex while blocked so that other operations on
|
||||
// the BufferQueue may succeed.
|
||||
status_t waitForFreeSlotThenRelock(const char* caller, bool async,
|
||||
int* found, status_t* returnFlags) const;
|
||||
|
||||
sp<BufferQueueCore> mCore;
|
||||
|
||||
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
|
||||
BufferQueueDefs::SlotsType& mSlots;
|
||||
|
||||
// This is a cached copy of the name stored in the BufferQueueCore.
|
||||
// It's updated during connect and dequeueBuffer (which should catch
|
||||
// most updates).
|
||||
String8 mConsumerName;
|
||||
|
||||
uint32_t mStickyTransform;
|
||||
|
||||
}; // class BufferQueueProducer
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
31
widget/gonk/nativewindow/GonkBufferQueueLL/BufferSlot.cpp
Normal file
31
widget/gonk/nativewindow/GonkBufferQueueLL/BufferSlot.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
#include <gui/BufferSlot.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
const char* BufferSlot::bufferStateName(BufferState state) {
|
||||
switch (state) {
|
||||
case BufferSlot::DEQUEUED: return "DEQUEUED";
|
||||
case BufferSlot::QUEUED: return "QUEUED";
|
||||
case BufferSlot::FREE: return "FREE";
|
||||
case BufferSlot::ACQUIRED: return "ACQUIRED";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
142
widget/gonk/nativewindow/GonkBufferQueueLL/BufferSlot.h
Normal file
142
widget/gonk/nativewindow/GonkBufferQueueLL/BufferSlot.h
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2014 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_BUFFERSLOT_H
|
||||
#define ANDROID_GUI_BUFFERSLOT_H
|
||||
|
||||
#include <ui/Fence.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class Fence;
|
||||
|
||||
struct BufferSlot {
|
||||
|
||||
BufferSlot()
|
||||
: mEglDisplay(EGL_NO_DISPLAY),
|
||||
mBufferState(BufferSlot::FREE),
|
||||
mRequestBufferCalled(false),
|
||||
mFrameNumber(0),
|
||||
mEglFence(EGL_NO_SYNC_KHR),
|
||||
mAcquireCalled(false),
|
||||
mNeedsCleanupOnRelease(false),
|
||||
mAttachedByConsumer(false) {
|
||||
}
|
||||
|
||||
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
|
||||
// if no buffer has been allocated.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
|
||||
EGLDisplay mEglDisplay;
|
||||
|
||||
// BufferState represents the different states in which a buffer slot
|
||||
// can be. All slots are initially FREE.
|
||||
enum BufferState {
|
||||
// FREE indicates that the buffer is available to be dequeued
|
||||
// by the producer. The buffer may be in use by the consumer for
|
||||
// a finite time, so the buffer must not be modified until the
|
||||
// associated fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by BufferQueue. It transitions to DEQUEUED
|
||||
// when dequeueBuffer is called.
|
||||
FREE = 0,
|
||||
|
||||
// DEQUEUED indicates that the buffer has been dequeued by the
|
||||
// producer, but has not yet been queued or canceled. The
|
||||
// producer may modify the buffer's contents as soon as the
|
||||
// associated ready fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by the producer. It can transition to
|
||||
// QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
|
||||
DEQUEUED = 1,
|
||||
|
||||
// QUEUED indicates that the buffer has been filled by the
|
||||
// producer and queued for use by the consumer. The buffer
|
||||
// contents may continue to be modified for a finite time, so
|
||||
// the contents must not be accessed until the associated fence
|
||||
// is signaled.
|
||||
//
|
||||
// The slot is "owned" by BufferQueue. It can transition to
|
||||
// ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
|
||||
// queued in asynchronous mode).
|
||||
QUEUED = 2,
|
||||
|
||||
// ACQUIRED indicates that the buffer has been acquired by the
|
||||
// consumer. As with QUEUED, the contents must not be accessed
|
||||
// by the consumer until the fence is signaled.
|
||||
//
|
||||
// The slot is "owned" by the consumer. It transitions to FREE
|
||||
// when releaseBuffer is called.
|
||||
ACQUIRED = 3
|
||||
};
|
||||
|
||||
static const char* bufferStateName(BufferState state);
|
||||
|
||||
// mBufferState is the current state of this buffer slot.
|
||||
BufferState mBufferState;
|
||||
|
||||
// mRequestBufferCalled is used for validating that the producer did
|
||||
// call requestBuffer() when told to do so. Technically this is not
|
||||
// needed but useful for debugging and catching producer bugs.
|
||||
bool mRequestBufferCalled;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot. This
|
||||
// is used to dequeue buffers in LRU order (useful because buffers
|
||||
// may be released before their release fence is signaled).
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mEglFence is the EGL sync object that must signal before the buffer
|
||||
// associated with this buffer slot may be dequeued. It is initialized
|
||||
// to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
|
||||
// new sync object in releaseBuffer. (This is deprecated in favor of
|
||||
// mFence, below.)
|
||||
EGLSyncKHR mEglFence;
|
||||
|
||||
// mFence is a fence which will signal when work initiated by the
|
||||
// previous owner of the buffer is finished. When the buffer is FREE,
|
||||
// the fence indicates when the consumer has finished reading
|
||||
// from the buffer, or when the producer has finished writing if it
|
||||
// called cancelBuffer after queueing some writes. When the buffer is
|
||||
// QUEUED, it indicates when the producer has finished filling the
|
||||
// buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
|
||||
// passed to the consumer or producer along with ownership of the
|
||||
// buffer, and mFence is set to NO_FENCE.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
|
||||
// Indicates whether this buffer needs to be cleaned up by the
|
||||
// consumer. This is set when a buffer in ACQUIRED state is freed.
|
||||
// It causes releaseBuffer to return STALE_BUFFER_SLOT.
|
||||
bool mNeedsCleanupOnRelease;
|
||||
|
||||
// Indicates whether the buffer was attached on the consumer side.
|
||||
// If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
|
||||
// to prevent the producer from using a stale cached buffer.
|
||||
bool mAttachedByConsumer;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
269
widget/gonk/nativewindow/GonkBufferQueueLL/ConsumerBase.cpp
Normal file
269
widget/gonk/nativewindow/GonkBufferQueueLL/ConsumerBase.cpp
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOG_TAG "ConsumerBase"
|
||||
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <hardware/hardware.h>
|
||||
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/ISurfaceComposer.h>
|
||||
#include <gui/SurfaceComposerClient.h>
|
||||
#include <gui/ConsumerBase.h>
|
||||
|
||||
#include <private/gui/ComposerService.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Trace.h>
|
||||
|
||||
// Macros for including the ConsumerBase name in log messages
|
||||
#define CB_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define CB_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define CB_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define CB_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define CB_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);
|
||||
}
|
||||
|
||||
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
|
||||
mAbandoned(false),
|
||||
mConsumer(bufferQueue) {
|
||||
// Choose a name using the PID and a process-unique ID.
|
||||
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||
|
||||
// Note that we can't create an sp<...>(this) in a ctor that will not keep a
|
||||
// reference once the ctor ends, as that would cause the refcount of 'this'
|
||||
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
|
||||
// that's what we create.
|
||||
wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
|
||||
sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
|
||||
|
||||
status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
|
||||
if (err != NO_ERROR) {
|
||||
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
|
||||
strerror(-err), err);
|
||||
} else {
|
||||
mConsumer->setConsumerName(mName);
|
||||
}
|
||||
}
|
||||
|
||||
ConsumerBase::~ConsumerBase() {
|
||||
CB_LOGV("~ConsumerBase");
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
// Verify that abandon() has been called before we get here. This should
|
||||
// be done by ConsumerBase::onLastStrongRef(), but it's possible for a
|
||||
// derived class to override that method and not call
|
||||
// ConsumerBase::onLastStrongRef().
|
||||
LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the "
|
||||
"consumer is not abandoned!", mName.string());
|
||||
}
|
||||
|
||||
void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) {
|
||||
abandon();
|
||||
}
|
||||
|
||||
void ConsumerBase::freeBufferLocked(int slotIndex) {
|
||||
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
|
||||
mSlots[slotIndex].mGraphicBuffer = 0;
|
||||
mSlots[slotIndex].mFence = Fence::NO_FENCE;
|
||||
mSlots[slotIndex].mFrameNumber = 0;
|
||||
}
|
||||
|
||||
void ConsumerBase::onFrameAvailable() {
|
||||
CB_LOGV("onFrameAvailable");
|
||||
|
||||
sp<FrameAvailableListener> listener;
|
||||
{ // scope for the lock
|
||||
Mutex::Autolock lock(mMutex);
|
||||
listener = mFrameAvailableListener.promote();
|
||||
}
|
||||
|
||||
if (listener != NULL) {
|
||||
CB_LOGV("actually calling onFrameAvailable");
|
||||
listener->onFrameAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsumerBase::onBuffersReleased() {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
CB_LOGV("onBuffersReleased");
|
||||
|
||||
if (mAbandoned) {
|
||||
// Nothing to do if we're already abandoned.
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t mask = 0;
|
||||
mConsumer->getReleasedBuffers(&mask);
|
||||
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
||||
if (mask & (1ULL << i)) {
|
||||
freeBufferLocked(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConsumerBase::onSidebandStreamChanged() {
|
||||
}
|
||||
|
||||
void ConsumerBase::abandon() {
|
||||
CB_LOGV("abandon");
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
if (!mAbandoned) {
|
||||
abandonLocked();
|
||||
mAbandoned = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ConsumerBase::abandonLocked() {
|
||||
CB_LOGV("abandonLocked");
|
||||
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
||||
freeBufferLocked(i);
|
||||
}
|
||||
// disconnect from the BufferQueue
|
||||
mConsumer->consumerDisconnect();
|
||||
mConsumer.clear();
|
||||
}
|
||||
|
||||
void ConsumerBase::setFrameAvailableListener(
|
||||
const wp<FrameAvailableListener>& listener) {
|
||||
CB_LOGV("setFrameAvailableListener");
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mFrameAvailableListener = listener;
|
||||
}
|
||||
|
||||
void ConsumerBase::dump(String8& result) const {
|
||||
dump(result, "");
|
||||
}
|
||||
|
||||
void ConsumerBase::dump(String8& result, const char* prefix) const {
|
||||
Mutex::Autolock _l(mMutex);
|
||||
dumpLocked(result, prefix);
|
||||
}
|
||||
|
||||
void ConsumerBase::dumpLocked(String8& result, const char* prefix) const {
|
||||
result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
|
||||
|
||||
if (!mAbandoned) {
|
||||
mConsumer->dump(result, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item,
|
||||
nsecs_t presentWhen) {
|
||||
status_t err = mConsumer->acquireBuffer(item, presentWhen);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (item->mGraphicBuffer != NULL) {
|
||||
mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer;
|
||||
}
|
||||
|
||||
mSlots[item->mBuf].mFrameNumber = item->mFrameNumber;
|
||||
mSlots[item->mBuf].mFence = item->mFence;
|
||||
|
||||
CB_LOGV("acquireBufferLocked: -> slot=%d/%" PRIu64,
|
||||
item->mBuf, item->mFrameNumber);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ConsumerBase::addReleaseFence(int slot,
|
||||
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
return addReleaseFenceLocked(slot, graphicBuffer, fence);
|
||||
}
|
||||
|
||||
status_t ConsumerBase::addReleaseFenceLocked(int slot,
|
||||
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
|
||||
CB_LOGV("addReleaseFenceLocked: slot=%d", slot);
|
||||
|
||||
// If consumer no longer tracks this graphicBuffer, we can safely
|
||||
// drop this fence, as it will never be received by the producer.
|
||||
if (!stillTracking(slot, graphicBuffer)) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (!mSlots[slot].mFence.get()) {
|
||||
mSlots[slot].mFence = fence;
|
||||
} else {
|
||||
sp<Fence> mergedFence = Fence::merge(
|
||||
String8::format("%.28s:%d", mName.string(), slot),
|
||||
mSlots[slot].mFence, fence);
|
||||
if (!mergedFence.get()) {
|
||||
CB_LOGE("failed to merge release fences");
|
||||
// synchronization is broken, the best we can do is hope fences
|
||||
// signal in order so the new fence will act like a union
|
||||
mSlots[slot].mFence = fence;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
mSlots[slot].mFence = mergedFence;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ConsumerBase::releaseBufferLocked(
|
||||
int slot, const sp<GraphicBuffer> graphicBuffer,
|
||||
EGLDisplay display, EGLSyncKHR eglFence) {
|
||||
// If consumer no longer tracks this graphicBuffer (we received a new
|
||||
// buffer on the same slot), the buffer producer is definitely no longer
|
||||
// tracking it.
|
||||
if (!stillTracking(slot, graphicBuffer)) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64,
|
||||
slot, mSlots[slot].mFrameNumber);
|
||||
status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber,
|
||||
display, eglFence, mSlots[slot].mFence);
|
||||
if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
|
||||
freeBufferLocked(slot);
|
||||
}
|
||||
|
||||
mSlots[slot].mFence = Fence::NO_FENCE;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool ConsumerBase::stillTracking(int slot,
|
||||
const sp<GraphicBuffer> graphicBuffer) {
|
||||
if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
|
||||
return false;
|
||||
}
|
||||
return (mSlots[slot].mGraphicBuffer != NULL &&
|
||||
mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle);
|
||||
}
|
||||
|
||||
} // namespace android
|
241
widget/gonk/nativewindow/GonkBufferQueueLL/ConsumerBase.h
Normal file
241
widget/gonk/nativewindow/GonkBufferQueueLL/ConsumerBase.h
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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_CONSUMERBASE_H
|
||||
#define ANDROID_GUI_CONSUMERBASE_H
|
||||
|
||||
#include <gui/BufferQueue.h>
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/threads.h>
|
||||
#include <gui/IConsumerListener.h>
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class String8;
|
||||
|
||||
// ConsumerBase is a base class for BufferQueue consumer end-points. It
|
||||
// handles common tasks like management of the connection to the BufferQueue
|
||||
// and the buffer pool.
|
||||
class ConsumerBase : public virtual RefBase,
|
||||
protected ConsumerListener {
|
||||
public:
|
||||
struct FrameAvailableListener : public virtual RefBase {
|
||||
// onFrameAvailable() is called each time an additional frame becomes
|
||||
// available for consumption. This means that frames that are queued
|
||||
// while in asynchronous mode only trigger the callback if no previous
|
||||
// frames are pending. Frames queued while in synchronous mode always
|
||||
// trigger the callback.
|
||||
//
|
||||
// This is called without any lock held and can be called concurrently
|
||||
// by multiple threads.
|
||||
virtual void onFrameAvailable() = 0;
|
||||
};
|
||||
|
||||
virtual ~ConsumerBase();
|
||||
|
||||
// abandon frees all the buffers and puts the ConsumerBase into the
|
||||
// 'abandoned' state. Once put in this state the ConsumerBase can never
|
||||
// leave it. When in the 'abandoned' state, all methods of the
|
||||
// IGraphicBufferProducer interface will fail with the NO_INIT error.
|
||||
//
|
||||
// Note that while calling this method causes all the buffers to be freed
|
||||
// from the perspective of the the ConsumerBase, if there are additional
|
||||
// references on the buffers (e.g. if a buffer is referenced by a client
|
||||
// or by OpenGL ES as a texture) then those buffer will remain allocated.
|
||||
void abandon();
|
||||
|
||||
// set the name of the ConsumerBase that will be used to identify it in
|
||||
// log messages.
|
||||
void setName(const String8& name);
|
||||
|
||||
// dump writes the current state to a string. Child classes should add
|
||||
// their state to the dump by overriding the dumpLocked method, which is
|
||||
// called by these methods after locking the mutex.
|
||||
void dump(String8& result) const;
|
||||
void dump(String8& result, const char* prefix) const;
|
||||
|
||||
// setFrameAvailableListener sets the listener object that will be notified
|
||||
// when a new frame becomes available.
|
||||
void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
|
||||
|
||||
private:
|
||||
ConsumerBase(const ConsumerBase&);
|
||||
void operator=(const ConsumerBase&);
|
||||
|
||||
protected:
|
||||
// ConsumerBase constructs a new ConsumerBase object to consume image
|
||||
// buffers from the given IGraphicBufferConsumer.
|
||||
// The controlledByApp flag indicates that this consumer is under the application's
|
||||
// control.
|
||||
ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
|
||||
|
||||
// onLastStrongRef gets called by RefBase just before the dtor of the most
|
||||
// derived class. It is used to clean up the buffers so that ConsumerBase
|
||||
// can coordinate the clean-up by calling into virtual methods implemented
|
||||
// by the derived classes. This would not be possible from the
|
||||
// ConsuemrBase dtor because by the time that gets called the derived
|
||||
// classes have already been destructed.
|
||||
//
|
||||
// This methods should not need to be overridden by derived classes, but
|
||||
// if they are overridden the ConsumerBase implementation must be called
|
||||
// from the derived class.
|
||||
virtual void onLastStrongRef(const void* id);
|
||||
|
||||
// Implementation of the IConsumerListener interface. These
|
||||
// calls are used to notify the ConsumerBase of asynchronous events in the
|
||||
// BufferQueue. The onFrameAvailable and onBuffersReleased methods should
|
||||
// not need to be overridden by derived classes, but if they are overridden
|
||||
// the ConsumerBase implementation must be called from the derived class.
|
||||
// The ConsumerBase version of onSidebandStreamChanged does nothing and can
|
||||
// be overriden by derived classes if they want the notification.
|
||||
virtual void onFrameAvailable();
|
||||
virtual void onBuffersReleased();
|
||||
virtual void onSidebandStreamChanged();
|
||||
|
||||
// freeBufferLocked frees up the given buffer slot. If the slot has been
|
||||
// initialized this will release the reference to the GraphicBuffer in that
|
||||
// slot. Otherwise it has no effect.
|
||||
//
|
||||
// Derived classes should override this method to clean up any state they
|
||||
// keep per slot. If it is overridden, the derived class's implementation
|
||||
// must call ConsumerBase::freeBufferLocked.
|
||||
//
|
||||
// This method must be called with mMutex locked.
|
||||
virtual void freeBufferLocked(int slotIndex);
|
||||
|
||||
// abandonLocked puts the BufferQueue into the abandoned state, causing
|
||||
// all future operations on it to fail. This method rather than the public
|
||||
// abandon method should be overridden by child classes to add abandon-
|
||||
// time behavior.
|
||||
//
|
||||
// Derived classes should override this method to clean up any object
|
||||
// state they keep (as opposed to per-slot state). If it is overridden,
|
||||
// the derived class's implementation must call ConsumerBase::abandonLocked.
|
||||
//
|
||||
// This method must be called with mMutex locked.
|
||||
virtual void abandonLocked();
|
||||
|
||||
// dumpLocked dumps the current state of the ConsumerBase object to the
|
||||
// result string. Each line is prefixed with the string pointed to by the
|
||||
// prefix argument. The buffer argument points to a buffer that may be
|
||||
// used for intermediate formatting data, and the size of that buffer is
|
||||
// indicated by the size argument.
|
||||
//
|
||||
// Derived classes should override this method to dump their internal
|
||||
// state. If this method is overridden the derived class's implementation
|
||||
// should call ConsumerBase::dumpLocked.
|
||||
//
|
||||
// This method must be called with mMutex locked.
|
||||
virtual void dumpLocked(String8& result, const char* prefix) const;
|
||||
|
||||
// acquireBufferLocked fetches the next buffer from the BufferQueue and
|
||||
// updates the buffer slot for the buffer returned.
|
||||
//
|
||||
// Derived classes should override this method to perform any
|
||||
// initialization that must take place the first time a buffer is assigned
|
||||
// to a slot. If it is overridden the derived class's implementation must
|
||||
// call ConsumerBase::acquireBufferLocked.
|
||||
virtual status_t acquireBufferLocked(IGraphicBufferConsumer::BufferItem *item,
|
||||
nsecs_t presentWhen);
|
||||
|
||||
// releaseBufferLocked relinquishes control over a buffer, returning that
|
||||
// control to the BufferQueue.
|
||||
//
|
||||
// Derived classes should override this method to perform any cleanup that
|
||||
// must take place when a buffer is released back to the BufferQueue. If
|
||||
// it is overridden the derived class's implementation must call
|
||||
// ConsumerBase::releaseBufferLocked.e
|
||||
virtual status_t releaseBufferLocked(int slot,
|
||||
const sp<GraphicBuffer> graphicBuffer,
|
||||
EGLDisplay display, EGLSyncKHR eglFence);
|
||||
|
||||
// returns true iff the slot still has the graphicBuffer in it.
|
||||
bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
|
||||
|
||||
// addReleaseFence* adds the sync points associated with a fence to the set
|
||||
// of sync points that must be reached before the buffer in the given slot
|
||||
// may be used after the slot has been released. This should be called by
|
||||
// derived classes each time some asynchronous work is kicked off that
|
||||
// references the buffer.
|
||||
status_t addReleaseFence(int slot,
|
||||
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
|
||||
status_t addReleaseFenceLocked(int slot,
|
||||
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
|
||||
|
||||
// Slot contains the information and object references that
|
||||
// ConsumerBase maintains about a BufferQueue buffer slot.
|
||||
struct Slot {
|
||||
// mGraphicBuffer is the Gralloc buffer store in the slot or NULL if
|
||||
// no Gralloc buffer is in the slot.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mFence is a fence which will signal when the buffer associated with
|
||||
// this buffer slot is no longer being used by the consumer and can be
|
||||
// overwritten. The buffer can be dequeued before the fence signals;
|
||||
// the producer is responsible for delaying writes until it signals.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// the frame number of the last acquired frame for this slot
|
||||
uint64_t mFrameNumber;
|
||||
};
|
||||
|
||||
// mSlots stores the buffers that have been allocated by the BufferQueue
|
||||
// for each buffer slot. It is initialized to null pointers, and gets
|
||||
// filled in with the result of BufferQueue::acquire when the
|
||||
// client dequeues a buffer from a
|
||||
// slot that has not yet been used. The buffer allocated to a slot will also
|
||||
// be replaced if the requested buffer usage or geometry differs from that
|
||||
// of the buffer allocated to a slot.
|
||||
Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS];
|
||||
|
||||
// mAbandoned indicates that the BufferQueue will no longer be used to
|
||||
// consume images buffers pushed to it using the IGraphicBufferProducer
|
||||
// interface. It is initialized to false, and set to true in the abandon
|
||||
// method. A BufferQueue that has been abandoned will return the NO_INIT
|
||||
// error from all IConsumerBase methods capable of returning an error.
|
||||
bool mAbandoned;
|
||||
|
||||
// mName is a string used to identify the ConsumerBase 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.
|
||||
wp<FrameAvailableListener> mFrameAvailableListener;
|
||||
|
||||
// The ConsumerBase has-a BufferQueue and is responsible for creating this object
|
||||
// if none is supplied
|
||||
sp<IGraphicBufferConsumer> mConsumer;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of ConsumerBase objects. It must be locked whenever the
|
||||
// member variables are accessed or when any of the *Locked methods are
|
||||
// called.
|
||||
//
|
||||
// This mutex is intended to be locked by derived classes.
|
||||
mutable Mutex mMutex;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_CONSUMERBASE_H
|
@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/NativeHandle.h>
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <binder/IInterface.h>
|
||||
|
||||
#include <gui/IConsumerListener.h>
|
||||
#include <gui/IGraphicBufferConsumer.h>
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
#include <ui/Fence.h>
|
||||
|
||||
#include <system/window.h>
|
||||
|
||||
namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
IGraphicBufferConsumer::BufferItem::BufferItem() :
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mIsAutoTimestamp(false),
|
||||
mFrameNumber(0),
|
||||
mBuf(INVALID_BUFFER_SLOT),
|
||||
mIsDroppable(false),
|
||||
mAcquireCalled(false),
|
||||
mTransformToDisplayInverse(false) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
|
||||
size_t IGraphicBufferConsumer::BufferItem::getPodSize() const {
|
||||
size_t c = sizeof(mCrop) +
|
||||
sizeof(mTransform) +
|
||||
sizeof(mScalingMode) +
|
||||
sizeof(mTimestamp) +
|
||||
sizeof(mIsAutoTimestamp) +
|
||||
sizeof(mFrameNumber) +
|
||||
sizeof(mBuf) +
|
||||
sizeof(mIsDroppable) +
|
||||
sizeof(mAcquireCalled) +
|
||||
sizeof(mTransformToDisplayInverse);
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t IGraphicBufferConsumer::BufferItem::getFlattenedSize() const {
|
||||
size_t c = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
c += mGraphicBuffer->getFlattenedSize();
|
||||
c = FlattenableUtils::align<4>(c);
|
||||
}
|
||||
if (mFence != 0) {
|
||||
c += mFence->getFlattenedSize();
|
||||
c = FlattenableUtils::align<4>(c);
|
||||
}
|
||||
return sizeof(int32_t) + c + getPodSize();
|
||||
}
|
||||
|
||||
size_t IGraphicBufferConsumer::BufferItem::getFdCount() const {
|
||||
size_t c = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
c += mGraphicBuffer->getFdCount();
|
||||
}
|
||||
if (mFence != 0) {
|
||||
c += mFence->getFdCount();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void writeBoolAsInt(void*& buffer, size_t& size, bool b) {
|
||||
FlattenableUtils::write(buffer, size, static_cast<int32_t>(b));
|
||||
}
|
||||
|
||||
static bool readBoolFromInt(void const*& buffer, size_t& size) {
|
||||
int32_t i;
|
||||
FlattenableUtils::read(buffer, size, i);
|
||||
return static_cast<bool>(i);
|
||||
}
|
||||
|
||||
status_t IGraphicBufferConsumer::BufferItem::flatten(
|
||||
void*& buffer, size_t& size, int*& fds, size_t& count) const {
|
||||
|
||||
// make sure we have enough space
|
||||
if (size < BufferItem::getFlattenedSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
// content flags are stored first
|
||||
uint32_t& flags = *static_cast<uint32_t*>(buffer);
|
||||
|
||||
// advance the pointer
|
||||
FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
|
||||
|
||||
flags = 0;
|
||||
if (mGraphicBuffer != 0) {
|
||||
status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
flags |= 1;
|
||||
}
|
||||
if (mFence != 0) {
|
||||
status_t err = mFence->flatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
flags |= 2;
|
||||
}
|
||||
|
||||
// check we have enough space (in case flattening the fence/graphicbuffer lied to us)
|
||||
if (size < getPodSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
FlattenableUtils::write(buffer, size, mCrop);
|
||||
FlattenableUtils::write(buffer, size, mTransform);
|
||||
FlattenableUtils::write(buffer, size, mScalingMode);
|
||||
FlattenableUtils::write(buffer, size, mTimestamp);
|
||||
writeBoolAsInt(buffer, size, mIsAutoTimestamp);
|
||||
FlattenableUtils::write(buffer, size, mFrameNumber);
|
||||
FlattenableUtils::write(buffer, size, mBuf);
|
||||
writeBoolAsInt(buffer, size, mIsDroppable);
|
||||
writeBoolAsInt(buffer, size, mAcquireCalled);
|
||||
writeBoolAsInt(buffer, size, mTransformToDisplayInverse);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t IGraphicBufferConsumer::BufferItem::unflatten(
|
||||
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
|
||||
|
||||
if (size < sizeof(uint32_t))
|
||||
return NO_MEMORY;
|
||||
|
||||
uint32_t flags = 0;
|
||||
FlattenableUtils::read(buffer, size, flags);
|
||||
|
||||
if (flags & 1) {
|
||||
mGraphicBuffer = new GraphicBuffer();
|
||||
status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
}
|
||||
|
||||
if (flags & 2) {
|
||||
mFence = new Fence();
|
||||
status_t err = mFence->unflatten(buffer, size, fds, count);
|
||||
if (err) return err;
|
||||
size -= FlattenableUtils::align<4>(buffer);
|
||||
}
|
||||
|
||||
// check we have enough space
|
||||
if (size < getPodSize()) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
FlattenableUtils::read(buffer, size, mCrop);
|
||||
FlattenableUtils::read(buffer, size, mTransform);
|
||||
FlattenableUtils::read(buffer, size, mScalingMode);
|
||||
FlattenableUtils::read(buffer, size, mTimestamp);
|
||||
mIsAutoTimestamp = readBoolFromInt(buffer, size);
|
||||
FlattenableUtils::read(buffer, size, mFrameNumber);
|
||||
FlattenableUtils::read(buffer, size, mBuf);
|
||||
mIsDroppable = readBoolFromInt(buffer, size);
|
||||
mAcquireCalled = readBoolFromInt(buffer, size);
|
||||
mTransformToDisplayInverse = readBoolFromInt(buffer, size);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
enum {
|
||||
ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
|
||||
DETACH_BUFFER,
|
||||
ATTACH_BUFFER,
|
||||
RELEASE_BUFFER,
|
||||
CONSUMER_CONNECT,
|
||||
CONSUMER_DISCONNECT,
|
||||
GET_RELEASED_BUFFERS,
|
||||
SET_DEFAULT_BUFFER_SIZE,
|
||||
SET_DEFAULT_MAX_BUFFER_COUNT,
|
||||
DISABLE_ASYNC_BUFFER,
|
||||
SET_MAX_ACQUIRED_BUFFER_COUNT,
|
||||
SET_CONSUMER_NAME,
|
||||
SET_DEFAULT_BUFFER_FORMAT,
|
||||
SET_CONSUMER_USAGE_BITS,
|
||||
SET_TRANSFORM_HINT,
|
||||
GET_SIDEBAND_STREAM,
|
||||
DUMP,
|
||||
};
|
||||
|
||||
|
||||
class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer>
|
||||
{
|
||||
public:
|
||||
BpGraphicBufferConsumer(const sp<IBinder>& impl)
|
||||
: BpInterface<IGraphicBufferConsumer>(impl)
|
||||
{
|
||||
}
|
||||
|
||||
virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt64(presentWhen);
|
||||
status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
result = reply.read(*buffer);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t detachBuffer(int slot) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(slot);
|
||||
status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.write(*buffer.get());
|
||||
status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
*slot = reply.readInt32();
|
||||
result = reply.readInt32();
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
|
||||
EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)),
|
||||
const sp<Fence>& releaseFence) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(buf);
|
||||
data.writeInt64(frameNumber);
|
||||
data.write(*releaseFence);
|
||||
status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeStrongBinder(consumer->asBinder());
|
||||
data.writeInt32(controlledByApp);
|
||||
status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t consumerDisconnect() {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t getReleasedBuffers(uint64_t* slotMask) {
|
||||
Parcel data, reply;
|
||||
if (slotMask == NULL) {
|
||||
ALOGE("getReleasedBuffers: slotMask must not be NULL");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
*slotMask = reply.readInt64();
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(w);
|
||||
data.writeInt32(h);
|
||||
status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setDefaultMaxBufferCount(int bufferCount) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(bufferCount);
|
||||
status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t disableAsyncBuffer() {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(maxAcquiredBuffers);
|
||||
status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual void setConsumerName(const String8& name) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeString8(name);
|
||||
remote()->transact(SET_CONSUMER_NAME, data, &reply);
|
||||
}
|
||||
|
||||
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(defaultFormat);
|
||||
status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setConsumerUsageBits(uint32_t usage) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(usage);
|
||||
status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual status_t setTransformHint(uint32_t hint) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeInt32(hint);
|
||||
status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
|
||||
if (result != NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual sp<NativeHandle> getSidebandStream() const {
|
||||
Parcel data, reply;
|
||||
status_t err;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) {
|
||||
return NULL;
|
||||
}
|
||||
sp<NativeHandle> stream;
|
||||
if (reply.readInt32()) {
|
||||
stream = NativeHandle::create(reply.readNativeHandle(), true);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
virtual void dump(String8& result, const char* prefix) const {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
|
||||
data.writeString8(result);
|
||||
data.writeString8(String8(prefix ? prefix : ""));
|
||||
remote()->transact(DUMP, data, &reply);
|
||||
reply.readString8();
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
status_t BnGraphicBufferConsumer::onTransact(
|
||||
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
|
||||
{
|
||||
switch(code) {
|
||||
case ACQUIRE_BUFFER: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
BufferItem item;
|
||||
int64_t presentWhen = data.readInt64();
|
||||
status_t result = acquireBuffer(&item, presentWhen);
|
||||
status_t err = reply->write(item);
|
||||
if (err) return err;
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case DETACH_BUFFER: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
int slot = data.readInt32();
|
||||
int result = detachBuffer(slot);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case ATTACH_BUFFER: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
sp<GraphicBuffer> buffer = new GraphicBuffer();
|
||||
data.read(*buffer.get());
|
||||
int slot;
|
||||
int result = attachBuffer(&slot, buffer);
|
||||
reply->writeInt32(slot);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case RELEASE_BUFFER: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
int buf = data.readInt32();
|
||||
uint64_t frameNumber = data.readInt64();
|
||||
sp<Fence> releaseFence = new Fence();
|
||||
status_t err = data.read(*releaseFence);
|
||||
if (err) return err;
|
||||
status_t result = releaseBuffer(buf, frameNumber,
|
||||
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case CONSUMER_CONNECT: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
|
||||
bool controlledByApp = data.readInt32();
|
||||
status_t result = consumerConnect(consumer, controlledByApp);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case CONSUMER_DISCONNECT: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
status_t result = consumerDisconnect();
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case GET_RELEASED_BUFFERS: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint64_t slotMask;
|
||||
status_t result = getReleasedBuffers(&slotMask);
|
||||
reply->writeInt64(slotMask);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_DEFAULT_BUFFER_SIZE: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint32_t w = data.readInt32();
|
||||
uint32_t h = data.readInt32();
|
||||
status_t result = setDefaultBufferSize(w, h);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_DEFAULT_MAX_BUFFER_COUNT: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint32_t bufferCount = data.readInt32();
|
||||
status_t result = setDefaultMaxBufferCount(bufferCount);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case DISABLE_ASYNC_BUFFER: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
status_t result = disableAsyncBuffer();
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_MAX_ACQUIRED_BUFFER_COUNT: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint32_t maxAcquiredBuffers = data.readInt32();
|
||||
status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_CONSUMER_NAME: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
setConsumerName( data.readString8() );
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_DEFAULT_BUFFER_FORMAT: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint32_t defaultFormat = data.readInt32();
|
||||
status_t result = setDefaultBufferFormat(defaultFormat);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_CONSUMER_USAGE_BITS: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint32_t usage = data.readInt32();
|
||||
status_t result = setConsumerUsageBits(usage);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case SET_TRANSFORM_HINT: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
uint32_t hint = data.readInt32();
|
||||
status_t result = setTransformHint(hint);
|
||||
reply->writeInt32(result);
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case DUMP: {
|
||||
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
|
||||
String8 result = data.readString8();
|
||||
String8 prefix = data.readString8();
|
||||
static_cast<IGraphicBufferConsumer*>(this)->dump(result, prefix);
|
||||
reply->writeString8(result);
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
}; // namespace android
|
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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_IGRAPHICBUFFERCONSUMER_H
|
||||
#define ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/Timers.h>
|
||||
|
||||
#include <binder/IInterface.h>
|
||||
#include <ui/Rect.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
namespace android {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class Fence;
|
||||
class GraphicBuffer;
|
||||
class IConsumerListener;
|
||||
class NativeHandle;
|
||||
|
||||
class IGraphicBufferConsumer : public IInterface {
|
||||
|
||||
public:
|
||||
|
||||
// public facing structure for BufferSlot
|
||||
class BufferItem : public Flattenable<BufferItem> {
|
||||
friend class Flattenable<BufferItem>;
|
||||
size_t getPodSize() const;
|
||||
size_t getFlattenedSize() const;
|
||||
size_t getFdCount() const;
|
||||
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
|
||||
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
|
||||
|
||||
public:
|
||||
// The default value of mBuf, used to indicate this doesn't correspond to a slot.
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
BufferItem();
|
||||
|
||||
// mGraphicBuffer points to the buffer allocated for this slot, or is NULL
|
||||
// if the buffer in this slot has been acquired in the past (see
|
||||
// BufferSlot.mAcquireCalled).
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mFence is a fence that will signal when the buffer is idle.
|
||||
sp<Fence> mFence;
|
||||
|
||||
// mCrop is the current crop rectangle for this buffer slot.
|
||||
Rect mCrop;
|
||||
|
||||
// mTransform is the current transform flags for this buffer slot.
|
||||
// refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
|
||||
uint32_t mTransform;
|
||||
|
||||
// mScalingMode is the current scaling mode for this buffer slot.
|
||||
// refer to NATIVE_WINDOW_SCALING_* in <window.h>
|
||||
uint32_t mScalingMode;
|
||||
|
||||
// mTimestamp is the current timestamp for this buffer slot. This gets
|
||||
// to set by queueBuffer each time this slot is queued. This value
|
||||
// is guaranteed to be monotonically increasing for each newly
|
||||
// acquired buffer.
|
||||
int64_t mTimestamp;
|
||||
|
||||
// mIsAutoTimestamp indicates whether mTimestamp was generated
|
||||
// automatically when the buffer was queued.
|
||||
bool mIsAutoTimestamp;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot.
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// mBuf is the slot index of this buffer (default INVALID_BUFFER_SLOT).
|
||||
int mBuf;
|
||||
|
||||
// mIsDroppable whether this buffer was queued with the
|
||||
// property that it can be replaced by a new buffer for the purpose of
|
||||
// making sure dequeueBuffer() won't block.
|
||||
// i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
|
||||
// was queued.
|
||||
bool mIsDroppable;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
|
||||
// Indicates this buffer must be transformed by the inverse transform of the screen
|
||||
// it is displayed onto. This is applied after mTransform.
|
||||
bool mTransformToDisplayInverse;
|
||||
};
|
||||
|
||||
enum {
|
||||
// Returned by releaseBuffer, after which the consumer must
|
||||
// free any references to the just-released buffer that it might have.
|
||||
STALE_BUFFER_SLOT = 1,
|
||||
// Returned by dequeueBuffer if there are no pending buffers available.
|
||||
NO_BUFFER_AVAILABLE,
|
||||
// Returned by dequeueBuffer if it's too early for the buffer to be acquired.
|
||||
PRESENT_LATER,
|
||||
};
|
||||
|
||||
// acquireBuffer attempts to acquire ownership of the next pending buffer in
|
||||
// the BufferQueue. If no buffer is pending then it returns
|
||||
// NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
|
||||
// information about the buffer is returned in BufferItem.
|
||||
//
|
||||
// If the buffer returned had previously been
|
||||
// acquired then the BufferItem::mGraphicBuffer field of buffer is set to
|
||||
// NULL and it is assumed that the consumer still holds a reference to the
|
||||
// buffer.
|
||||
//
|
||||
// If presentWhen is non-zero, it indicates the time when the buffer will
|
||||
// be displayed on screen. If the buffer's timestamp is farther in the
|
||||
// future, the buffer won't be acquired, and PRESENT_LATER will be
|
||||
// returned. The presentation time is in nanoseconds, and the time base
|
||||
// is CLOCK_MONOTONIC.
|
||||
//
|
||||
// Return of NO_ERROR means the operation completed as normal.
|
||||
//
|
||||
// Return of a positive value means the operation could not be completed
|
||||
// at this time, but the user should try again later:
|
||||
// * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer)
|
||||
// * PRESENT_LATER - the buffer's timestamp is farther in the future
|
||||
//
|
||||
// Return of a negative value means an error has occurred:
|
||||
// * INVALID_OPERATION - too many buffers have been acquired
|
||||
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
|
||||
|
||||
// detachBuffer attempts to remove all ownership of the buffer in the given
|
||||
// slot from the buffer queue. If this call succeeds, the slot will be
|
||||
// freed, and there will be no way to obtain the buffer from this interface.
|
||||
// The freed slot will remain unallocated until either it is selected to
|
||||
// hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
|
||||
// to the slot. The buffer must have already been acquired.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * BAD_VALUE - the given slot number is invalid, either because it is
|
||||
// out of the range [0, NUM_BUFFER_SLOTS) or because the slot
|
||||
// it refers to is not currently acquired.
|
||||
virtual status_t detachBuffer(int slot) = 0;
|
||||
|
||||
// attachBuffer attempts to transfer ownership of a buffer to the buffer
|
||||
// queue. If this call succeeds, it will be as if this buffer was acquired
|
||||
// from the returned slot number. As such, this call will fail if attaching
|
||||
// this buffer would cause too many buffers to be simultaneously acquired.
|
||||
//
|
||||
// If the buffer is successfully attached, its frameNumber is initialized
|
||||
// to 0. This must be passed into the releaseBuffer call or else the buffer
|
||||
// will be deallocated as stale.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * BAD_VALUE - outSlot or buffer were NULL
|
||||
// * INVALID_OPERATION - cannot attach the buffer because it would cause too
|
||||
// many buffers to be acquired.
|
||||
// * NO_MEMORY - no free slots available
|
||||
virtual status_t attachBuffer(int *outSlot,
|
||||
const sp<GraphicBuffer>& buffer) = 0;
|
||||
|
||||
// releaseBuffer releases a buffer slot from the consumer back to the
|
||||
// BufferQueue. This may be done while the buffer's contents are still
|
||||
// being accessed. The fence will signal when the buffer is no longer
|
||||
// in use. frameNumber is used to indentify the exact buffer returned.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Return of NO_ERROR means the operation completed as normal.
|
||||
//
|
||||
// Return of a positive value means the operation could not be completed
|
||||
// at this time, but the user should try again later:
|
||||
// * STALE_BUFFER_SLOT - see above (second paragraph)
|
||||
//
|
||||
// Return of a negative value means an error has occurred:
|
||||
// * BAD_VALUE - one of the following could've happened:
|
||||
// * the buffer slot was invalid
|
||||
// * the fence was NULL
|
||||
// * the buffer slot specified is not in the acquired state
|
||||
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
|
||||
EGLDisplay display, EGLSyncKHR fence,
|
||||
const sp<Fence>& releaseFence) = 0;
|
||||
|
||||
// consumerConnect connects a consumer to the BufferQueue. Only one
|
||||
// consumer may be connected, and when that consumer disconnects the
|
||||
// BufferQueue is placed into the "abandoned" state, causing most
|
||||
// interactions with the BufferQueue by the producer to fail.
|
||||
// controlledByApp indicates whether the consumer is controlled by
|
||||
// the application.
|
||||
//
|
||||
// consumer may not be NULL.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * NO_INIT - the buffer queue has been abandoned
|
||||
// * BAD_VALUE - a NULL consumer was provided
|
||||
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
|
||||
|
||||
// consumerDisconnect disconnects a consumer from the BufferQueue. All
|
||||
// buffers will be freed and the BufferQueue is placed in the "abandoned"
|
||||
// state, causing most interactions with the BufferQueue by the producer to
|
||||
// fail.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * BAD_VALUE - no consumer is currently connected
|
||||
virtual status_t consumerDisconnect() = 0;
|
||||
|
||||
// getReleasedBuffers sets the value pointed to by slotMask to a bit set.
|
||||
// Each bit index with a 1 corresponds to a released buffer slot with that
|
||||
// index value. In particular, a released buffer is one that has
|
||||
// been released by the BufferQueue but have not yet been released by the consumer.
|
||||
//
|
||||
// This should be called from the onBuffersReleased() callback.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * NO_INIT - the buffer queue has been abandoned.
|
||||
virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
|
||||
|
||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||
// dequeueBuffer when a width and height of zero is requested. Default
|
||||
// is 1x1.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * BAD_VALUE - either w or h was zero
|
||||
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
|
||||
|
||||
// setDefaultMaxBufferCount sets the default value for the maximum buffer
|
||||
// count (the initial default is 2). If the producer has requested a
|
||||
// buffer count using setBufferCount, the default buffer count will only
|
||||
// take effect if the producer sets the count back to zero.
|
||||
//
|
||||
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * BAD_VALUE - bufferCount was out of range (see above).
|
||||
virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0;
|
||||
|
||||
// disableAsyncBuffer disables the extra buffer used in async mode
|
||||
// (when both producer and consumer have set their "isControlledByApp"
|
||||
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
|
||||
//
|
||||
// This can only be called before consumerConnect().
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * INVALID_OPERATION - attempting to call this after consumerConnect.
|
||||
virtual status_t disableAsyncBuffer() = 0;
|
||||
|
||||
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
|
||||
// be acquired by the consumer at one time (default 1). This call will
|
||||
// fail if a producer is connected to the BufferQueue.
|
||||
//
|
||||
// maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an error has occurred:
|
||||
// * BAD_VALUE - maxAcquiredBuffers was out of range (see above).
|
||||
// * INVALID_OPERATION - attempting to call this after a producer connected.
|
||||
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
|
||||
|
||||
// setConsumerName sets the name used in logging
|
||||
virtual void setConsumerName(const String8& name) = 0;
|
||||
|
||||
// setDefaultBufferFormat allows the BufferQueue to create
|
||||
// GraphicBuffers of a defaultFormat if no format is specified
|
||||
// in dequeueBuffer. Formats are enumerated in graphics.h; the
|
||||
// initial default is HAL_PIXEL_FORMAT_RGBA_8888.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an unknown error has occurred.
|
||||
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0;
|
||||
|
||||
// setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
|
||||
// These are merged with the bits passed to dequeueBuffer. The values are
|
||||
// enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an unknown error has occurred.
|
||||
virtual status_t setConsumerUsageBits(uint32_t usage) = 0;
|
||||
|
||||
// setTransformHint bakes in rotation to buffers so overlays can be used.
|
||||
// The values are enumerated in window.h, e.g.
|
||||
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
|
||||
//
|
||||
// Return of a value other than NO_ERROR means an unknown error has occurred.
|
||||
virtual status_t setTransformHint(uint32_t hint) = 0;
|
||||
|
||||
// Retrieve the sideband buffer stream, if any.
|
||||
virtual sp<NativeHandle> getSidebandStream() const = 0;
|
||||
|
||||
// dump state into a string
|
||||
virtual void dump(String8& result, const char* prefix) const = 0;
|
||||
|
||||
public:
|
||||
DECLARE_META_INTERFACE(GraphicBufferConsumer);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class BnGraphicBufferConsumer : public BnInterface<IGraphicBufferConsumer>
|
||||
{
|
||||
public:
|
||||
virtual status_t onTransact( uint32_t code,
|
||||
const Parcel& data,
|
||||
Parcel* reply,
|
||||
uint32_t flags = 0);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
|
Loading…
Reference in New Issue
Block a user