summaryrefslogtreecommitdiffstats
path: root/services/surfaceflinger
diff options
context:
space:
mode:
Diffstat (limited to 'services/surfaceflinger')
-rw-r--r--services/surfaceflinger/DisplayDevice.cpp19
-rw-r--r--services/surfaceflinger/DisplayDevice.h2
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplaySurface.h12
-rw-r--r--services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp4
-rw-r--r--services/surfaceflinger/DisplayHardware/FramebufferSurface.h1
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp15
-rw-r--r--services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp380
-rw-r--r--services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h174
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp5
9 files changed, 591 insertions, 21 deletions
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2eae9c2..c67f4d8 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 eefc107..748be1a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -119,6 +119,8 @@ public:
int32_t getHwcDisplayId() const { return mHwcDisplayId; }
const wp<IBinder>& 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 2eca3cb..b0f460d 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<IGraphicBufferProducer> 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 10bca38..3f51345 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -72,6 +72,10 @@ sp<IGraphicBufferProducer> 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 c86e9ae..92a7f9b 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -41,6 +41,7 @@ public:
virtual sp<IGraphicBufferProducer> 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 5082192..ca98133 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 2838b23..90bfce7 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 <cutils/log.h>
-#include <gui/IGraphicBufferProducer.h>
+#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 "<INVALID>";
+ }
+}
+
VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
const sp<IGraphicBufferProducer>& 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<IGraphicBufferProducer> VirtualDisplaySurface::getIGraphicBufferProducer() const {
- return mSink;
+ if (mDisplayId >= 0) {
+ return static_cast<IGraphicBufferProducer*>(
+ const_cast<VirtualDisplaySurface*>(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<Fence> 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<GraphicBuffer> fbBuffer = mProducerBuffers[mFbProducerSlot];
+ sp<GraphicBuffer> 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<Fence> 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<Fence> 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<GraphicBuffer>* 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>* 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<uint32_t>(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>* 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(&timestamp, &crop, &scalingMode, &transform,
+ &mFbFence);
+
+ mFbProducerSlot = pslot;
+ }
+
+ *output = mQueueBufferOutput;
+ return NO_ERROR;
+}
+
+void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& 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 f321795..2b4cf8f 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 <gui/ConsumerBase.h>
+#include <gui/IGraphicBufferProducer.h>
+
#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<IGraphicBufferProducer>& sink,
const String8& name);
+ //
+ // DisplaySurface interface
+ //
virtual sp<IGraphicBufferProducer> 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<IGraphicBufferProducer> mSink;
+ //
+ // IGraphicBufferProducer interface, used by the GLES driver.
+ //
+ virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
+ virtual status_t setBufferCount(int bufferCount);
+ virtual status_t dequeueBuffer(int* pslot, sp<Fence>* 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>& 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>* 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<IGraphicBufferProducer> 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<GraphicBuffer> 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<Fence> 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 ecf9fa7..9adabe8 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<mDisplays.size() ; dpy++) {
+ sp<const DisplayDevice> hw(mDisplays[dpy]);
+ hw->prepareFrame(hwc);
+ }
}
}