diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 2eae9c297..c67f4d8f8 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -188,6 +188,25 @@ void DisplayDevice::flip(const Region& dirty) const mPageFlipCount++; } +status_t DisplayDevice::prepareFrame(const HWComposer& hwc) const { + DisplaySurface::CompositionType compositionType; + bool haveGles = hwc.hasGlesComposition(mHwcDisplayId); + bool haveHwc = hwc.hasHwcComposition(mHwcDisplayId); + if (haveGles && haveHwc) { + compositionType = DisplaySurface::COMPOSITION_MIXED; + } else if (haveGles) { + compositionType = DisplaySurface::COMPOSITION_GLES; + } else if (haveHwc) { + compositionType = DisplaySurface::COMPOSITION_HWC; + } else { + // Nothing to do -- when turning the screen off we get a frame like + // this. Call it a HWC frame since we won't be doing any GLES work but + // will do a prepare/set cycle. + compositionType = DisplaySurface::COMPOSITION_HWC; + } + return mDisplaySurface->prepareFrame(compositionType); +} + void DisplayDevice::swapBuffers(HWComposer& hwc) const { // We need to call eglSwapBuffers() unless: // (a) there was no GLES composition this frame, or diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index eefc10709..748be1ab5 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -119,6 +119,8 @@ public: int32_t getHwcDisplayId() const { return mHwcDisplayId; } const wp& getDisplayToken() const { return mDisplayToken; } + status_t prepareFrame(const HWComposer& hwc) const; + void swapBuffers(HWComposer& hwc) const; status_t compositionComplete() const; diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h index 2eca3cbfb..b0f460d30 100644 --- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h @@ -32,6 +32,18 @@ class DisplaySurface : public virtual RefBase { public: virtual sp getIGraphicBufferProducer() const = 0; + // prepareFrame is called after the composition configuration is known but + // before composition takes place. The DisplaySurface can use the + // composition type to decide how to manage the flow of buffers between + // GLES and HWC for this frame. + enum CompositionType { + COMPOSITION_UNKNOWN = 0, + COMPOSITION_GLES = 1, + COMPOSITION_HWC = 2, + COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC + }; + virtual status_t prepareFrame(CompositionType compositionType) = 0; + // Should be called when composition rendering is complete for a frame (but // eglSwapBuffers hasn't necessarily been called). Required by certain // older drivers for synchronization. diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 10bca383c..3f513452e 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -72,6 +72,10 @@ sp FramebufferSurface::getIGraphicBufferProducer() const return getBufferQueue(); } +status_t FramebufferSurface::prepareFrame(CompositionType compositionType) { + return NO_ERROR; +} + status_t FramebufferSurface::advanceFrame() { // Once we remove FB HAL support, we can call nextBuffer() from here // instead of using onFrameAvailable(). No real benefit, except it'll be diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index c86e9ae25..92a7f9b1e 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -41,6 +41,7 @@ public: virtual sp getIGraphicBufferProducer() const; + virtual status_t prepareFrame(CompositionType compositionType); virtual status_t compositionComplete(); virtual status_t advanceFrame(); virtual void onFrameCommitted(); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 5082192cf..ca98133b5 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -16,9 +16,6 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -// Uncomment this to remove support for HWC_DEVICE_API_VERSION_0_3 and older -#define HWC_REMOVE_DEPRECATED_VERSIONS 1 - #include #include #include @@ -50,9 +47,9 @@ namespace android { -// This is not a real HWC version. It's used for in-development features that -// haven't been committed to a specific real HWC version. -#define HWC_DEVICE_API_VERSION_1_EXP HARDWARE_DEVICE_API_VERSION_2(1, 0xFF, HWC_HEADER_VERSION) +#ifndef HWC_DEVICE_API_VERSION_1_3 +#define HWC_DEVICE_API_VERSION_1_3 HARDWARE_DEVICE_API_VERSION_2(1, 3, HWC_HEADER_VERSION) +#endif #define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION @@ -156,8 +153,8 @@ HWComposer::HWComposer( // the number of displays we actually have depends on the // hw composer version - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_EXP)) { - // 1.?? adds support for virtual displays + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + // 1.3 adds support for virtual displays mNumDisplays = MAX_DISPLAYS; } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { // 1.1 adds support for multiple displays @@ -582,7 +579,7 @@ status_t HWComposer::prepare() { } mLists[i] = disp.list; if (mLists[i]) { - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_EXP)) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { mLists[i]->outbuf = NULL; mLists[i]->outbufAcquireFenceFd = -1; } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 2838b2303..90bfce7b3 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -14,27 +14,89 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 #include "VirtualDisplaySurface.h" - -#include -#include +#include "HWComposer.h" // --------------------------------------------------------------------------- namespace android { // --------------------------------------------------------------------------- +#define VDS_LOGE(msg, ...) ALOGE("[%s] "msg, \ + mDisplayName.string(), ##__VA_ARGS__) +#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] "msg, \ + mDisplayName.string(), ##__VA_ARGS__) +#define VDS_LOGV(msg, ...) ALOGV("[%s] "msg, \ + mDisplayName.string(), ##__VA_ARGS__) + +static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) { + switch (type) { + case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN"; + case DisplaySurface::COMPOSITION_GLES: return "GLES"; + case DisplaySurface::COMPOSITION_HWC: return "HWC"; + case DisplaySurface::COMPOSITION_MIXED: return "MIXED"; + default: return ""; + } +} + VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, const sp& sink, const String8& name) -: mSink(sink) +: ConsumerBase(new BufferQueue(true)), + mHwc(hwc), + mDisplayId(dispId), + mDisplayName(name), + mProducerUsage(GRALLOC_USAGE_HW_COMPOSER), + mProducerSlotSource(0), + mDbgState(DBG_STATE_IDLE), + mDbgLastCompositionType(COMPOSITION_UNKNOWN) { - LOG_ALWAYS_FATAL_IF(dispId >= 0); + mSource[SOURCE_SINK] = sink; + mSource[SOURCE_SCRATCH] = mBufferQueue; + + resetPerFrameState(); + + int sinkWidth, sinkHeight; + mSource[SOURCE_SINK]->query(NATIVE_WINDOW_WIDTH, &sinkWidth); + mSource[SOURCE_SINK]->query(NATIVE_WINDOW_HEIGHT, &sinkHeight); + + ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string()); + mBufferQueue->setConsumerName(ConsumerBase::mName); + mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); + mBufferQueue->setDefaultBufferSize(sinkWidth, sinkHeight); + mBufferQueue->setDefaultMaxBufferCount(2); } VirtualDisplaySurface::~VirtualDisplaySurface() { } sp VirtualDisplaySurface::getIGraphicBufferProducer() const { - return mSink; + if (mDisplayId >= 0) { + return static_cast( + const_cast(this)); + } else { + // There won't be any interaction with HWC for this virtual display, + // so the GLES driver can pass buffers directly to the sink. + return mSource[SOURCE_SINK]; + } +} + +status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { + if (mDisplayId < 0) + return NO_ERROR; + + VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE, + "Unexpected prepareFrame() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_PREPARED; + + mCompositionType = compositionType; + + if (mCompositionType != mDbgLastCompositionType) { + VDS_LOGV("prepareFrame: composition type changed to %s", + dbgCompositionTypeStr(mCompositionType)); + mDbgLastCompositionType = mCompositionType; + } + + return NO_ERROR; } status_t VirtualDisplaySurface::compositionComplete() { @@ -42,15 +104,319 @@ status_t VirtualDisplaySurface::compositionComplete() { } status_t VirtualDisplaySurface::advanceFrame() { - return NO_ERROR; + if (mDisplayId < 0) + return NO_ERROR; + + if (mCompositionType == COMPOSITION_HWC) { + VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, + "Unexpected advanceFrame() in %s state on HWC frame", + dbgStateStr()); + } else { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE, + "Unexpected advanceFrame() in %s state on GLES/MIXED frame", + dbgStateStr()); + } + mDbgState = DBG_STATE_HWC; + + status_t result; + sp outFence; + if (mCompositionType != COMPOSITION_GLES) { + // Dequeue an output buffer from the sink + uint32_t transformHint, numPendingBuffers; + mQueueBufferOutput.deflate(&mSinkBufferWidth, &mSinkBufferHeight, + &transformHint, &numPendingBuffers); + int sslot; + result = dequeueBuffer(SOURCE_SINK, 0, &sslot, &outFence); + if (result < 0) + return result; + mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot); + } + + if (mCompositionType == COMPOSITION_HWC) { + // We just dequeued the output buffer, use it for FB as well + mFbProducerSlot = mOutputProducerSlot; + mFbFence = outFence; + } else if (mCompositionType == COMPOSITION_GLES) { + mOutputProducerSlot = mFbProducerSlot; + outFence = mFbFence; + } else { + // mFbFence and mFbProducerSlot were set in queueBuffer, + // and mOutputProducerSlot and outFence were set above when dequeueing + // the sink buffer. + } + + if (mFbProducerSlot < 0 || mOutputProducerSlot < 0) { + // Last chance bailout if something bad happened earlier. For example, + // in a GLES configuration, if the sink disappears then dequeueBuffer + // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger + // will soldier on. So we end up here without a buffer. There should + // be lots of scary messages in the log just before this. + VDS_LOGE("advanceFrame: no buffer, bailing out"); + return NO_MEMORY; + } + + sp fbBuffer = mProducerBuffers[mFbProducerSlot]; + sp outBuffer = mProducerBuffers[mOutputProducerSlot]; + VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)", + mFbProducerSlot, fbBuffer.get(), + mOutputProducerSlot, outBuffer.get()); + + result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer); + if (result == NO_ERROR) { + result = mHwc.setOutputBuffer(mDisplayId, outFence, outBuffer); + } + + return result; } void VirtualDisplaySurface::onFrameCommitted() { + if (mDisplayId < 0) + return; + + VDS_LOGW_IF(mDbgState != DBG_STATE_HWC, + "Unexpected onFrameCommitted() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_IDLE; + + sp fbFence = mHwc.getAndResetReleaseFence(mDisplayId); + if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) { + // release the scratch buffer back to the pool + Mutex::Autolock lock(mMutex); + int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); + VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot); + addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence); + releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot], + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + } + + if (mOutputProducerSlot >= 0) { + int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot); + QueueBufferOutput qbo; + sp outFence = mHwc.getLastRetireFence(mDisplayId); + VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot); + status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, + QueueBufferInput(systemTime(), + Rect(mSinkBufferWidth, mSinkBufferHeight), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, outFence), + &qbo); + if (result == NO_ERROR) { + updateQueueBufferOutput(qbo); + } + } + + resetPerFrameState(); } void VirtualDisplaySurface::dump(String8& result) const { } +status_t VirtualDisplaySurface::requestBuffer(int pslot, + sp* outBuf) { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, + "Unexpected requestBuffer pslot=%d in %s state", + pslot, dbgStateStr()); + + *outBuf = mProducerBuffers[pslot]; + return NO_ERROR; +} + +status_t VirtualDisplaySurface::setBufferCount(int bufferCount) { + return mSource[SOURCE_SINK]->setBufferCount(bufferCount); +} + +status_t VirtualDisplaySurface::dequeueBuffer(Source source, + uint32_t format, int* sslot, sp* fence) { + status_t result = mSource[source]->dequeueBuffer(sslot, fence, + mSinkBufferWidth, mSinkBufferHeight, format, mProducerUsage); + if (result < 0) + return result; + int pslot = mapSource2ProducerSlot(source, *sslot); + VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d", + dbgSourceStr(source), *sslot, pslot, result); + uint32_t sourceBit = static_cast(source) << pslot; + + if ((mProducerSlotSource & (1u << pslot)) != sourceBit) { + // This slot was previously dequeued from the other source; must + // re-request the buffer. + result |= BUFFER_NEEDS_REALLOCATION; + mProducerSlotSource &= ~(1u << pslot); + mProducerSlotSource |= sourceBit; + } + + if (result & RELEASE_ALL_BUFFERS) { + for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if ((mProducerSlotSource & (1u << i)) == sourceBit) + mProducerBuffers[i].clear(); + } + } + if (result & BUFFER_NEEDS_REALLOCATION) { + mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]); + VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p", + dbgSourceStr(source), pslot, mProducerBuffers[pslot].get()); + } + + return result; +} + +status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp* fence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, + "Unexpected dequeueBuffer() in %s state", dbgStateStr()); + mDbgState = DBG_STATE_GLES; + + VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage); + + mProducerUsage = usage | GRALLOC_USAGE_HW_COMPOSER; + Source source = fbSourceForCompositionType(mCompositionType); + if (source == SOURCE_SINK) { + mSinkBufferWidth = w; + mSinkBufferHeight = h; + } + + int sslot; + status_t result = dequeueBuffer(source, format, &sslot, fence); + if (result >= 0) { + *pslot = mapSource2ProducerSlot(source, sslot); + } + return result; +} + +status_t VirtualDisplaySurface::queueBuffer(int pslot, + const QueueBufferInput& input, QueueBufferOutput* output) { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, + "Unexpected queueBuffer(pslot=%d) in %s state", pslot, + dbgStateStr()); + mDbgState = DBG_STATE_GLES_DONE; + + VDS_LOGV("queueBuffer pslot=%d", pslot); + + status_t result; + if (mCompositionType == COMPOSITION_MIXED) { + // Queue the buffer back into the scratch pool + QueueBufferOutput scratchQBO; + int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot); + result = mBufferQueue->queueBuffer(sslot, input, &scratchQBO); + if (result != NO_ERROR) + return result; + + // Now acquire the buffer from the scratch pool -- should be the same + // slot and fence as we just queued. + Mutex::Autolock lock(mMutex); + BufferQueue::BufferItem item; + result = acquireBufferLocked(&item); + if (result != NO_ERROR) + return result; + VDS_LOGW_IF(item.mBuf != sslot, + "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d", + item.mBuf, sslot); + mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf); + mFbFence = mSlots[item.mBuf].mFence; + + } else { + LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES, + "Unexpected queueBuffer in state %s for compositionType %s", + dbgStateStr(), dbgCompositionTypeStr(mCompositionType)); + + // Extract the GLES release fence for HWC to acquire + int64_t timestamp; + Rect crop; + int scalingMode; + uint32_t transform; + input.deflate(×tamp, &crop, &scalingMode, &transform, + &mFbFence); + + mFbProducerSlot = pslot; + } + + *output = mQueueBufferOutput; + return NO_ERROR; +} + +void VirtualDisplaySurface::cancelBuffer(int pslot, const sp& fence) { + VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, + "Unexpected cancelBuffer(pslot=%d) in %s state", pslot, + dbgStateStr()); + VDS_LOGV("cancelBuffer pslot=%d", pslot); + Source source = fbSourceForCompositionType(mCompositionType); + return mSource[source]->cancelBuffer( + mapProducer2SourceSlot(source, pslot), fence); +} + +int VirtualDisplaySurface::query(int what, int* value) { + return mSource[SOURCE_SINK]->query(what, value); +} + +status_t VirtualDisplaySurface::setSynchronousMode(bool enabled) { + return mSource[SOURCE_SINK]->setSynchronousMode(enabled); +} + +status_t VirtualDisplaySurface::connect(int api, QueueBufferOutput* output) { + QueueBufferOutput qbo; + status_t result = mSource[SOURCE_SINK]->connect(api, &qbo); + if (result == NO_ERROR) { + updateQueueBufferOutput(qbo); + *output = mQueueBufferOutput; + } + return result; +} + +status_t VirtualDisplaySurface::disconnect(int api) { + return mSource[SOURCE_SINK]->disconnect(api); +} + +void VirtualDisplaySurface::updateQueueBufferOutput( + const QueueBufferOutput& qbo) { + uint32_t w, h, transformHint, numPendingBuffers; + qbo.deflate(&w, &h, &transformHint, &numPendingBuffers); + mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers); +} + +void VirtualDisplaySurface::resetPerFrameState() { + mCompositionType = COMPOSITION_UNKNOWN; + mSinkBufferWidth = 0; + mSinkBufferHeight = 0; + mFbFence = Fence::NO_FENCE; + mFbProducerSlot = -1; + mOutputProducerSlot = -1; +} + +// This slot mapping function is its own inverse, so two copies are unnecessary. +// Both are kept to make the intent clear where the function is called, and for +// the (unlikely) chance that we switch to a different mapping function. +int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) { + if (source == SOURCE_SCRATCH) { + return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1; + } else { + return sslot; + } +} +int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) { + return mapSource2ProducerSlot(source, pslot); +} + +VirtualDisplaySurface::Source +VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) { + return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK; +} + +const char* VirtualDisplaySurface::dbgStateStr() const { + switch (mDbgState) { + case DBG_STATE_IDLE: return "IDLE"; + case DBG_STATE_PREPARED: return "PREPARED"; + case DBG_STATE_GLES: return "GLES"; + case DBG_STATE_GLES_DONE: return "GLES_DONE"; + case DBG_STATE_HWC: return "HWC"; + default: return "INVALID"; + } +} + +const char* VirtualDisplaySurface::dbgSourceStr(Source s) { + switch (s) { + case SOURCE_SINK: return "SINK"; + case SOURCE_SCRATCH: return "SCRATCH"; + default: return "INVALID"; + } +} + // --------------------------------------------------------------------------- } // namespace android // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index f32179576..2b4cf8f8f 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -17,6 +17,9 @@ #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H +#include +#include + #include "DisplaySurface.h" // --------------------------------------------------------------------------- @@ -25,26 +28,187 @@ namespace android { class HWComposer; -/* This DisplaySurface implementation is a stub used for developing HWC - * virtual display support. It is currently just a passthrough. +/* This DisplaySurface implementation supports virtual displays, where GLES + * and/or HWC compose into a buffer that is then passed to an arbitrary + * consumer (the sink) running in another process. + * + * The simplest case is when the virtual display will never use the h/w + * composer -- either the h/w composer doesn't support writing to buffers, or + * there are more virtual displays than it supports simultaneously. In this + * case, the GLES driver works directly with the output buffer queue, and + * calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do + * nothing. + * + * If h/w composer might be used, then each frame will fall into one of three + * configurations: GLES-only, HWC-only, and MIXED composition. In all of these, + * we must provide a FB target buffer and output buffer for the HWC set() call. + * + * In GLES-only composition, the GLES driver is given a buffer from the sink to + * render into. When the GLES driver queues the buffer to the + * VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of + * immediately queueing it to the sink. The buffer is used as both the FB + * target and output buffer for HWC, though on these frames the HWC doesn't + * do any work for this display and doesn't write to the output buffer. After + * composition is complete, the buffer is queued to the sink. + * + * In HWC-only composition, the VirtualDisplaySurface dequeues a buffer from + * the sink and passes it to HWC as both the FB target buffer and output + * buffer. The HWC doesn't need to read from the FB target buffer, but does + * write to the output buffer. After composition is complete, the buffer is + * queued to the sink. + * + * On MIXED frames, things become more complicated, since some h/w composer + * implementations can't read from and write to the same buffer. This class has + * an internal BufferQueue that it uses as a scratch buffer pool. The GLES + * driver is given a scratch buffer to render into. When it finishes rendering, + * the buffer is queued and then immediately acquired by the + * VirtualDisplaySurface. The scratch buffer is then used as the FB target + * buffer for HWC, and a separate buffer is dequeued from the sink and used as + * the HWC output buffer. When HWC composition is complete, the scratch buffer + * is released and the output buffer is queued to the sink. */ -class VirtualDisplaySurface : public DisplaySurface { +class VirtualDisplaySurface : public DisplaySurface, + private BnGraphicBufferProducer, + private ConsumerBase { public: VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, const sp& sink, const String8& name); + // + // DisplaySurface interface + // virtual sp getIGraphicBufferProducer() const; - + virtual status_t prepareFrame(CompositionType compositionType); virtual status_t compositionComplete(); virtual status_t advanceFrame(); virtual void onFrameCommitted(); virtual void dump(String8& result) const; private: + enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1}; + virtual ~VirtualDisplaySurface(); - sp mSink; + // + // IGraphicBufferProducer interface, used by the GLES driver. + // + virtual status_t requestBuffer(int pslot, sp* outBuf); + virtual status_t setBufferCount(int bufferCount); + virtual status_t dequeueBuffer(int* pslot, sp* fence, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t queueBuffer(int pslot, + const QueueBufferInput& input, QueueBufferOutput* output); + virtual void cancelBuffer(int pslot, const sp& 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); + + // + // Utility methods + // + static Source fbSourceForCompositionType(CompositionType type); + status_t dequeueBuffer(Source source, uint32_t format, + int* sslot, sp* fence); + void updateQueueBufferOutput(const QueueBufferOutput& qbo); + void resetPerFrameState(); + + // Both the sink and scratch buffer pools have their own set of slots + // ("source slots", or "sslot"). We have to merge these into the single + // set of slots used by the GLES producer ("producer slots" or "pslot") and + // internally in the VirtualDisplaySurface. To minimize the number of times + // a producer slot switches which source it comes from, we map source slot + // numbers to producer slot numbers differently for each source. + static int mapSource2ProducerSlot(Source source, int sslot); + static int mapProducer2SourceSlot(Source source, int pslot); + + // + // Immutable after construction + // + HWComposer& mHwc; + const int32_t mDisplayId; + const String8 mDisplayName; + sp mSource[2]; // indexed by SOURCE_* + + // + // Inter-frame state + // + + // To avoid buffer reallocations, we track the buffer usage requested by + // the GLES driver in dequeueBuffer so we can use the same flags on + // HWC-only frames. + uint32_t mProducerUsage; + + // Since we present a single producer interface to the GLES driver, but + // are internally muxing between the sink and scratch producers, we have + // to keep track of which source last returned each producer slot from + // dequeueBuffer. Each bit in mLastSlotSource corresponds to a producer + // slot. Both mProducerSlotSource and mProducerBuffers are indexed by a + // "producer slot"; see the mapSlot*() functions. + uint32_t mProducerSlotSource; + sp mProducerBuffers[BufferQueue::NUM_BUFFER_SLOTS]; + + // The QueueBufferOutput with the latest info from the sink, and with the + // transform hint cleared. Since we defer queueBuffer from the GLES driver + // to the sink, we have to return the previous version. + QueueBufferOutput mQueueBufferOutput; + + // + // Intra-frame state + // + + // Composition type and GLES buffer source for the current frame. + // Valid after prepareFrame(), cleared in onFrameCommitted. + CompositionType mCompositionType; + + // Details of the current sink buffer. These become valid when a buffer is + // dequeued from the sink, and are used when queueing the buffer. + uint32_t mSinkBufferWidth, mSinkBufferHeight; + + // mFbFence is the fence HWC should wait for before reading the framebuffer + // target buffer. + sp mFbFence; + + // Producer slot numbers for the buffers to use for HWC framebuffer target + // and output. + int mFbProducerSlot; + int mOutputProducerSlot; + + // Debug only -- track the sequence of events in each frame so we can make + // sure they happen in the order we expect. This class implicitly models + // a state machine; this enum/variable makes it explicit. + // + // +-----------+-------------------+-------------+ + // | State | Event || Next State | + // +-----------+-------------------+-------------+ + // | IDLE | prepareFrame || PREPARED | + // | PREPARED | dequeueBuffer [1] || GLES | + // | PREPARED | advanceFrame [2] || HWC | + // | GLES | queueBuffer || GLES_DONE | + // | GLES_DONE | advanceFrame || HWC | + // | HWC | onFrameCommitted || IDLE | + // +-----------+-------------------++------------+ + // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames. + // [2] COMPOSITION_HWC frames. + // + enum DbgState { + // no buffer dequeued, don't know anything about the next frame + DBG_STATE_IDLE, + // no buffer dequeued, but we know the buffer source for the frame + DBG_STATE_PREPARED, + // GLES driver has a buffer dequeued + DBG_STATE_GLES, + // GLES driver has queued the buffer, we haven't sent it to HWC yet + DBG_STATE_GLES_DONE, + // HWC has the buffer for this frame + DBG_STATE_HWC, + }; + DbgState mDbgState; + CompositionType mDbgLastCompositionType; + + const char* dbgStateStr() const; + static const char* dbgSourceStr(Source s); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ecf9fa783..9adabe848 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -895,6 +895,11 @@ void SurfaceFlinger::setUpHWComposer() { status_t err = hwc.prepare(); ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err)); + + for (size_t dpy=0 ; dpy hw(mDisplays[dpy]); + hw->prepareFrame(hwc); + } } }