diff options
Diffstat (limited to 'services/surfaceflinger')
7 files changed, 483 insertions, 10 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 6f89ffa..30a01be 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -14,6 +14,7 @@ LOCAL_SRC_FILES:= \ SurfaceFlingerConsumer.cpp \ SurfaceTextureLayer.cpp \ Transform.cpp \ + DisplayHardware/BufferQueueInterposer.cpp \ DisplayHardware/FramebufferSurface.cpp \ DisplayHardware/HWComposer.cpp \ DisplayHardware/PowerHAL.cpp \ diff --git a/services/surfaceflinger/DisplayHardware/BufferQueueInterposer.cpp b/services/surfaceflinger/DisplayHardware/BufferQueueInterposer.cpp new file mode 100644 index 0000000..d8ad224 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/BufferQueueInterposer.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 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. + */ + +#undef LOG_TAG +#define LOG_TAG "BQInterposer" + +#include "BufferQueueInterposer.h" + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +#define BQI_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BQI_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BQI_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BQI_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define BQI_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +BufferQueueInterposer::BufferQueueInterposer( + const sp<IGraphicBufferProducer>& sink, const String8& name) +: mSink(sink), + mName(name), + mAcquired(false) +{ + BQI_LOGV("BufferQueueInterposer sink=%p", sink.get()); + + // We need one additional dequeued buffer beyond what the source needs. + // To have more than one (the default), we must call setBufferCount. But + // we have no way of knowing what the sink has set as the minimum buffer + // count, so if we just call setBufferCount(3) it may fail (and does, on + // one device using a video encoder sink). So far on the devices we care + // about, this is the smallest value that works. + // + // TODO: Change IGraphicBufferProducer and implementations to support this. + // Maybe change it so both the consumer and producer declare how many + // buffers they need, and the IGBP adds them? Then BQInterposer would just + // add 1 to the source's buffer count. + mSink->setBufferCount(6); +} + +BufferQueueInterposer::~BufferQueueInterposer() { + Mutex::Autolock lock(mMutex); + flushQueuedBuffersLocked(); + BQI_LOGV("~BufferQueueInterposer"); +} + +status_t BufferQueueInterposer::requestBuffer(int slot, + sp<GraphicBuffer>* outBuf) { + BQI_LOGV("requestBuffer slot=%d", slot); + Mutex::Autolock lock(mMutex); + + if (size_t(slot) >= mBuffers.size()) { + size_t size = mBuffers.size(); + mBuffers.insertAt(size, size - slot + 1); + } + sp<GraphicBuffer>& buf = mBuffers.editItemAt(slot); + + status_t result = mSink->requestBuffer(slot, &buf); + *outBuf = buf; + return result; +} + +status_t BufferQueueInterposer::setBufferCount(int bufferCount) { + BQI_LOGV("setBufferCount count=%d", bufferCount); + Mutex::Autolock lock(mMutex); + + bufferCount += 1; + + status_t result = flushQueuedBuffersLocked(); + if (result != NO_ERROR) + return result; + + result = mSink->setBufferCount(bufferCount); + if (result != NO_ERROR) + return result; + + for (size_t i = 0; i < mBuffers.size(); i++) + mBuffers.editItemAt(i).clear(); + ssize_t n = mBuffers.resize(bufferCount); + result = (n < 0) ? n : result; + + return result; +} + +status_t BufferQueueInterposer::dequeueBuffer(int* slot, sp<Fence>* fence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + BQI_LOGV("dequeueBuffer %ux%u fmt=%u usage=%#x", w, h, format, usage); + return mSink->dequeueBuffer(slot, fence, w, h, format, usage); +} + +status_t BufferQueueInterposer::queueBuffer(int slot, + const QueueBufferInput& input, QueueBufferOutput* output) { + BQI_LOGV("queueBuffer slot=%d", slot); + Mutex::Autolock lock(mMutex); + mQueue.push(QueuedBuffer(slot, input)); + *output = mQueueBufferOutput; + return NO_ERROR; +} + +void BufferQueueInterposer::cancelBuffer(int slot, const sp<Fence>& fence) { + BQI_LOGV("cancelBuffer slot=%d", slot); + mSink->cancelBuffer(slot, fence); +} + +int BufferQueueInterposer::query(int what, int* value) { + BQI_LOGV("query what=%d", what); + return mSink->query(what, value); +} + +status_t BufferQueueInterposer::setSynchronousMode(bool enabled) { + BQI_LOGV("setSynchronousMode %s", enabled ? "true" : "false"); + return mSink->setSynchronousMode(enabled); +} + +status_t BufferQueueInterposer::connect(int api, QueueBufferOutput* output) { + BQI_LOGV("connect api=%d", api); + Mutex::Autolock lock(mMutex); + status_t result = mSink->connect(api, &mQueueBufferOutput); + if (result == NO_ERROR) { + *output = mQueueBufferOutput; + } + return result; +} + +status_t BufferQueueInterposer::disconnect(int api) { + BQI_LOGV("disconnect: api=%d", api); + Mutex::Autolock lock(mMutex); + flushQueuedBuffersLocked(); + return mSink->disconnect(api); +} + +status_t BufferQueueInterposer::pullEmptyBuffer() { + status_t result; + + int slot; + sp<Fence> fence; + result = dequeueBuffer(&slot, &fence, 0, 0, 0, 0); + if (result == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + sp<GraphicBuffer> buffer; + result = requestBuffer(slot, &buffer); + } else if (result != NO_ERROR) { + return result; + } + + uint32_t w, h, transformHint, numPendingBuffers; + mQueueBufferOutput.deflate(&w, &h, &transformHint, &numPendingBuffers); + + IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(w, h), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, fence); + IGraphicBufferProducer::QueueBufferOutput qbo; + result = queueBuffer(slot, qbi, &qbo); + if (result != NO_ERROR) + return result; + + return NO_ERROR; +} + +status_t BufferQueueInterposer::acquireBuffer(sp<GraphicBuffer>* buf, + sp<Fence>* fence) { + Mutex::Autolock lock(mMutex); + if (mQueue.empty()) { + BQI_LOGV("acquireBuffer: no buffers available"); + return NO_BUFFER_AVAILABLE; + } + if (mAcquired) { + BQI_LOGE("acquireBuffer: buffer already acquired"); + return BUFFER_ALREADY_ACQUIRED; + } + BQI_LOGV("acquireBuffer: acquiring slot %d", mQueue[0].slot); + + *buf = mBuffers[mQueue[0].slot]; + *fence = mQueue[0].fence; + mAcquired = true; + return NO_ERROR; +} + +status_t BufferQueueInterposer::releaseBuffer(const sp<Fence>& fence) { + Mutex::Autolock lock(mMutex); + if (!mAcquired) { + BQI_LOGE("releaseBuffer: releasing a non-acquired buffer"); + return BUFFER_NOT_ACQUIRED; + } + BQI_LOGV("releaseBuffer: releasing slot %d to sink", mQueue[0].slot); + + const QueuedBuffer& b = mQueue[0]; + status_t result = mSink->queueBuffer(b.slot, + QueueBufferInput(b.timestamp, b.crop, b.scalingMode, + b.transform, b.fence), + &mQueueBufferOutput); + mQueue.removeAt(0); + mAcquired = false; + + return result; +} + +status_t BufferQueueInterposer::flushQueuedBuffersLocked() { + if (mAcquired) { + BQI_LOGE("flushQueuedBuffersLocked: buffer acquired, can't flush"); + return INVALID_OPERATION; + } + + status_t result = NO_ERROR; + for (size_t i = 0; i < mQueue.size(); i++) { + const QueuedBuffer& b = mQueue[i]; + BQI_LOGV("flushing queued slot %d to sink", b.slot); + status_t err = mSink->queueBuffer(b.slot, + QueueBufferInput(b.timestamp, b.crop, b.scalingMode, + b.transform, b.fence), + &mQueueBufferOutput); + if (err != NO_ERROR && result == NO_ERROR) // latch first error + result = err; + } + mQueue.clear(); + return result; +} + +// --------------------------------------------------------------------------- +} // namespace android +// --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/BufferQueueInterposer.h b/services/surfaceflinger/DisplayHardware/BufferQueueInterposer.h new file mode 100644 index 0000000..7208630 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/BufferQueueInterposer.h @@ -0,0 +1,152 @@ +/* + * Copyright 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_SF_BUFFERQUEUEINTERPOSER_H +#define ANDROID_SF_BUFFERQUEUEINTERPOSER_H + +#include <gui/IGraphicBufferProducer.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +// BufferQueueInterposers introduce an extra stage between a buffer producer +// (the source) and a buffer consumer (the sink), which communicate via the +// IGraphicBufferProducer interface. It is designed to be as transparent as +// possible to both endpoints, so that they can work the same whether an +// interposer is present or not. +// +// When the interpose is present, the source queues buffers to the +// IGraphicBufferProducer implemented by BufferQueueInterposer. A client of +// the BufferQueueInterposer can acquire each buffer in turn and read or +// modify it, releasing the buffer when finished. When the buffer is released, +// the BufferQueueInterposer queues it to the original IGraphicBufferProducer +// interface representing the sink. +// +// A BufferQueueInterposer can be used to do additional rendering to a buffer +// before it is consumed -- essentially pipelining two producers. As an +// example, SurfaceFlinger uses this to implement mixed GLES and HWC +// compositing to the same buffer for virtual displays. If it used two separate +// buffer queues, then in GLES-only or mixed GLES+HWC compositing, the HWC +// would have to copy the GLES output buffer to the HWC output buffer, using +// more bandwidth than having HWC do additional composition "in place" on the +// GLES output buffer. +// +// The goal for this class is to be usable in a variety of situations and be +// part of libgui. But both the interface and implementation need some +// iteration before then, so for now it should only be used by +// VirtualDisplaySurface, which is why it's currently in SurfaceFlinger. +// +// Some of the problems that still need to be solved are: +// +// - Refactor the interposer interface along with BufferQueue and ConsumerBase, +// so that there is a common interface for the consumer end of a queue. The +// existing interfaces have some problems when the implementation isn't the +// final consumer. +// +// - The interposer needs at least one buffer in addition to those used by the +// source and sink. setBufferCount and QueueBufferOutput both need to +// account for this. It's not possible currently to do this generically, +// since we can't find out how many buffers the source and sink need. (See +// the horrible hack in the BufferQueueInterposer constructor). +// +// - Abandoning, disconnecting, and connecting need to pass through somehow. +// There needs to be a way to tell the interposer client to release its +// buffer immediately so it can be queued/released, e.g. when the source +// calls disconnect(). +// +// - Right now the source->BQI queue is synchronous even if the BQI->sink +// queue is asynchronous. Need to figure out how asynchronous should behave +// and implement that. + +class BufferQueueInterposer : public BnGraphicBufferProducer { +public: + BufferQueueInterposer(const sp<IGraphicBufferProducer>& sink, + const String8& name); + + // + // IGraphicBufferProducer interface + // + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* outBuf); + virtual status_t setBufferCount(int bufferCount); + virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t queueBuffer(int slot, + const QueueBufferInput& input, QueueBufferOutput* output); + virtual void cancelBuffer(int slot, const sp<Fence>& fence); + virtual int query(int what, int* value); + virtual status_t setSynchronousMode(bool enabled); + virtual status_t connect(int api, QueueBufferOutput* output); + virtual status_t disconnect(int api); + + // + // Interposer interface + // + + enum { + NO_BUFFER_AVAILABLE = 2, // matches BufferQueue + BUFFER_NOT_ACQUIRED, + BUFFER_ALREADY_ACQUIRED, + }; + + // Acquire the oldest queued buffer. If no buffers are pending, returns + // NO_BUFFER_AVAILABLE. If a buffer is currently acquired, returns + // BUFFER_ALREADY_ACQUIRED. + status_t acquireBuffer(sp<GraphicBuffer>* buf, sp<Fence>* fence); + + // Release the currently acquired buffer, queueing it to the sink. If the + // current buffer hasn't been acquired, returns BUFFER_NOT_ACQUIRED. + status_t releaseBuffer(const sp<Fence>& fence); + + // pullEmptyBuffer dequeues a buffer from the sink, then immediately + // queues it to the interposer. This makes a buffer available for the + // client to acquire even if the source hasn't queued one. + status_t pullEmptyBuffer(); + +private: + struct QueuedBuffer { + QueuedBuffer(): slot(-1) {} + QueuedBuffer(int slot, const QueueBufferInput& qbi): slot(slot) { + qbi.deflate(×tamp, &crop, &scalingMode, &transform, &fence); + } + int slot; + int64_t timestamp; + Rect crop; + int scalingMode; + uint32_t transform; + sp<Fence> fence; + }; + + virtual ~BufferQueueInterposer(); + status_t flushQueuedBuffersLocked(); + + const sp<IGraphicBufferProducer> mSink; + String8 mName; + + Mutex mMutex; + Vector<sp<GraphicBuffer> > mBuffers; + Vector<QueuedBuffer> mQueue; + bool mAcquired; + QueueBufferOutput mQueueBufferOutput; +}; + +// --------------------------------------------------------------------------- +} // namespace android +// --------------------------------------------------------------------------- + +#endif // ANDROID_SF_BUFFERQUEUEINTERPOSER_H diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 4b6c413..eeb1fef 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -655,6 +655,18 @@ status_t HWComposer::commit() { mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW); } + // For virtual displays, the framebufferTarget buffer also serves as + // the HWC output buffer, so we need to copy the buffer handle and + // dup() the acquire fence. + for (size_t i=HWC_NUM_DISPLAY_TYPES; i<mNumDisplays; i++) { + DisplayData& disp(mDisplayData[i]); + if (disp.framebufferTarget) { + mLists[i]->outbuf = disp.framebufferTarget->handle; + mLists[i]->outbufAcquireFenceFd = + dup(disp.framebufferTarget->acquireFenceFd); + } + } + err = mHwc->set(mHwc, mNumDisplays, mLists); for (size_t i=0 ; i<mNumDisplays ; i++) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 9816a45..92e4cbf 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -112,7 +112,7 @@ public: // does this display have layers handled by GLES bool hasGlesComposition(int32_t id) const; - // get the releaseFence file descriptor for the given display + // get the releaseFence file descriptor for a display's framebuffer layer. // the release fence is only valid after commit() int getAndResetReleaseFenceFd(int32_t id); diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index d30e9bf..433e1eb 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <gui/IGraphicBufferProducer.h> #include "VirtualDisplaySurface.h" +#include "HWComposer.h" // --------------------------------------------------------------------------- namespace android { @@ -25,15 +25,22 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int disp, const sp<IGraphicBufferProducer>& sink, const String8& name) : mHwc(hwc), mDisplayId(disp), - mSink(sink), - mName(name) + mSource(new BufferQueueInterposer(sink, name)), + mName(name), + mReleaseFence(Fence::NO_FENCE) {} VirtualDisplaySurface::~VirtualDisplaySurface() { + if (mAcquiredBuffer != NULL) { + status_t result = mSource->releaseBuffer(mReleaseFence); + ALOGE_IF(result != NO_ERROR, "VirtualDisplaySurface \"%s\": " + "failed to release previous buffer: %d", + mName.string(), result); + } } sp<IGraphicBufferProducer> VirtualDisplaySurface::getIGraphicBufferProducer() const { - return mSink; + return mSource; } status_t VirtualDisplaySurface::compositionComplete() { @@ -41,13 +48,49 @@ status_t VirtualDisplaySurface::compositionComplete() { } status_t VirtualDisplaySurface::advanceFrame() { - return NO_ERROR; + Mutex::Autolock lock(mMutex); + status_t result = NO_ERROR; + + if (mAcquiredBuffer != NULL) { + result = mSource->releaseBuffer(mReleaseFence); + ALOGE_IF(result != NO_ERROR, "VirtualDisplaySurface \"%s\": " + "failed to release previous buffer: %d", + mName.string(), result); + mAcquiredBuffer.clear(); + mReleaseFence = Fence::NO_FENCE; + } + + sp<Fence> fence; + result = mSource->acquireBuffer(&mAcquiredBuffer, &fence); + if (result == BufferQueueInterposer::NO_BUFFER_AVAILABLE) { + result = mSource->pullEmptyBuffer(); + if (result != NO_ERROR) + return result; + result = mSource->acquireBuffer(&mAcquiredBuffer, &fence); + } + if (result != NO_ERROR) + return result; + + return mHwc.fbPost(mDisplayId, fence, mAcquiredBuffer); } status_t VirtualDisplaySurface::setReleaseFenceFd(int fenceFd) { if (fenceFd >= 0) { - ALOGW("[%s] unexpected release fence", mName.string()); - close(fenceFd); + sp<Fence> fence(new Fence(fenceFd)); + Mutex::Autolock lock(mMutex); + sp<Fence> mergedFence = Fence::merge( + String8::format("VirtualDisplaySurface \"%s\"", + mName.string()), + mReleaseFence, fence); + if (!mergedFence->isValid()) { + ALOGE("VirtualDisplaySurface \"%s\": failed to merge release fence", + mName.string()); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mReleaseFence = fence; + return BAD_VALUE; + } + mReleaseFence = mergedFence; } return NO_ERROR; } diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index db886ed..66f8580 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -17,7 +17,7 @@ #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H -#include <utils/String8.h> +#include "BufferQueueInterposer.h" #include "DisplaySurface.h" // --------------------------------------------------------------------------- @@ -26,6 +26,28 @@ namespace android { class HWComposer; +/* This DisplaySurface implementation uses a BufferQueueInterposer to pass + * partially- or fully-composited buffers from the OpenGL ES driver to + * HWComposer to use as the output buffer for virtual displays. Allowing HWC + * to compose into the same buffer that contains GLES results saves bandwidth + * compared to having two separate BufferQueues for frames with at least some + * GLES composition. + * + * The alternative would be to have two complete BufferQueues, one from GLES + * to HWC and one from HWC to the virtual display sink (e.g. video encoder). + * For GLES-only frames, the same bandwidth saving could be achieved if buffers + * could be acquired from the GLES->HWC queue and inserted into the HWC->sink + * queue. That would be complicated and doesn't help the mixed GLES+HWC case. + * + * On frames with no GLES composition, the VirtualDisplaySurface dequeues a + * buffer directly from the sink IGraphicBufferProducer and passes it to HWC, + * bypassing the GLES driver. This is only guaranteed to work if + * eglSwapBuffers doesn't immediately dequeue a buffer for the next frame, + * since we can't rely on being able to dequeue more than one buffer at a time. + * + * TODO(jessehall): Add a libgui test that ensures that EGL/GLES do lazy + * dequeBuffers; we've wanted to require that for other reasons anyway. + */ class VirtualDisplaySurface : public DisplaySurface { public: VirtualDisplaySurface(HWComposer& hwc, int disp, @@ -45,8 +67,13 @@ private: // immutable after construction HWComposer& mHwc; int mDisplayId; - sp<IGraphicBufferProducer> mSink; + sp<BufferQueueInterposer> mSource; String8 mName; + + // mutable, must be synchronized with mMutex + Mutex mMutex; + sp<GraphicBuffer> mAcquiredBuffer; + sp<Fence> mReleaseFence; }; // --------------------------------------------------------------------------- |