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 (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); // Allow queuing to sink buffer if mMustRecompose is true or // mForceHwcCopy is true. This is required to support Miracast WFD Sink // Initiatied Pause/Resume feature support if (mForceHwcCopy || mMustRecompose) { status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, QueueBufferInput( systemTime(), false /* isAutoTimestamp */, Rect(mSinkBufferWidth, mSinkBufferHeight), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */, true /* async*/, outFence), &qbo); if (result == NO_ERROR) { updateQueueBufferOutput(qbo); } } else { // If the surface hadn't actually been updated, then we only went // through the motions of updating the display to keep our state // machine happy. We cancel the buffer to avoid triggering another // re-composition and causing an infinite loop. mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence); } } resetPerFrameState(); }
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); }
status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { if (mDisplayId < 0) return mSource[SOURCE_SINK]->queueBuffer(pslot, input, 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 = mSource[SOURCE_SCRATCH]->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); BufferItem item; result = acquireBufferLocked(&item, 0); 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; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop; int scalingMode; uint32_t transform; bool async; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &async, &mFbFence); mFbProducerSlot = pslot; mOutputFence = mFbFence; } *output = mQueueBufferOutput; return NO_ERROR; }
status_t VirtualDisplaySurface::dequeueBuffer(Source source, uint32_t format, uint32_t usage, int* sslot, sp<Fence>* fence) { LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId); // Don't let a slow consumer block us bool async = (source == SOURCE_SINK); status_t result = mSource[source]->dequeueBuffer(sslot, fence, async, mSinkBufferWidth, mSinkBufferHeight, format, usage); 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); uint64_t sourceBit = static_cast<uint64_t>(source) << pslot; if ((mProducerSlotSource & (1ULL << pslot)) != sourceBit) { // This slot was previously dequeued from the other source; must // re-request the buffer. result |= BUFFER_NEEDS_REALLOCATION; mProducerSlotSource &= ~(1ULL << pslot); mProducerSlotSource |= sourceBit; } if (result & RELEASE_ALL_BUFFERS) { for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if ((mProducerSlotSource & (1ULL << i)) == sourceBit) mProducerBuffers[i].clear(); } } if (result & BUFFER_NEEDS_REALLOCATION) { result = mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]); if (result < 0) { mProducerBuffers[pslot].clear(); mSource[source]->cancelBuffer(*sslot, *fence); return result; } VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#x", dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(), mProducerBuffers[pslot]->getPixelFormat(), mProducerBuffers[pslot]->getUsage()); } return result; }
status_t VirtualDisplaySurface::advanceFrame() { if (mDisplayId < 0) return NO_ERROR; // When mForceHwcCopy is true, we override the composition type to MIXED. // Therefore, we need to check whether we are in this scenario and add // checks to satisfy the state machine requirements and reduce log spam. // In particular, by setting mForceHwcCopy we can now expect to get an // advanceFrame when composition type is MIXED and our previous state was // PREPARED or GLES_DONE. if (mForceHwcCopy && (mCompositionType == COMPOSITION_MIXED)) { bool isValidState = (mDbgState == DBG_STATE_PREPARED) || (mDbgState == DBG_STATE_GLES_DONE); VDS_LOGW_IF(!isValidState, "Unexpected advanceFrame() in %s state on %s frame", dbgStateStr(), (mDbgState == DBG_STATE_PREPARED) ? "HWC" : "GLES/MIXED"); } else 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; if (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 = mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL); sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot]; VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)", mFbProducerSlot, fbBuffer.get(), mOutputProducerSlot, outBuffer.get()); // At this point we know the output buffer acquire fence, // so update HWC state with it. mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer); status_t result = NO_ERROR; if (fbBuffer != NULL) { result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer); } 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(), false /* isAutoTimestamp */, Rect(mSinkBufferWidth, mSinkBufferHeight), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */, true /* async*/, outFence), &qbo); if (result == NO_ERROR) { updateQueueBufferOutput(qbo); } } resetPerFrameState(); }
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { if (mDisplayId < 0) return NO_ERROR; VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN, "Unexpected prepareFrame() in %s state", dbgStateStr()); mDbgState = DBG_STATE_PREPARED; mCompositionType = compositionType; if (sForceHwcCopy && mCompositionType == COMPOSITION_GLES) { // Some hardware can do RGB->YUV conversion more efficiently in hardware // controlled by HWC than in hardware controlled by the video encoder. // Forcing GLES-composed frames to go through an extra copy by the HWC // allows the format conversion to happen there, rather than passing RGB // directly to the consumer. // // On the other hand, when the consumer prefers RGB or can consume RGB // inexpensively, this forces an unnecessary copy. mCompositionType = COMPOSITION_MIXED; } if (mCompositionType != mDbgLastCompositionType) { VDS_LOGV("prepareFrame: composition type changed to %s", dbgCompositionTypeStr(mCompositionType)); mDbgLastCompositionType = mCompositionType; } if (mCompositionType != COMPOSITION_GLES && (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) { // We must have just switched from GLES-only to MIXED or HWC // composition. Stop using the format and usage requested by the GLES // driver; they may be suboptimal when HWC is writing to the output // buffer. For example, if the output is going to a video encoder, and // HWC can write directly to YUV, some hardware can skip a // memory-to-memory RGB-to-YUV conversion step. // // If we just switched *to* GLES-only mode, we'll change the // format/usage and get a new buffer when the GLES driver calls // dequeueBuffer(). mOutputFormat = mDefaultOutputFormat; mOutputUsage = GRALLOC_USAGE_HW_COMPOSER; refreshOutputBuffer(); } return NO_ERROR; }
status_t VirtualDisplaySurface::advanceFrame() { 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; if (mOutputProducerSlot < 0 || (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 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 = mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL); sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot]; VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)", mFbProducerSlot, fbBuffer.get(), mOutputProducerSlot, outBuffer.get()); // At this point we know the output buffer acquire fence, // so update HWC state with it. mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer); status_t result = NO_ERROR; if (fbBuffer != NULL) { result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer); } return result; }
status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool async, 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_LOGW_IF(!async, "EGL called dequeueBuffer with !async despite eglSwapInterval(0)"); VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage); status_t result = NO_ERROR; Source source = fbSourceForCompositionType(mCompositionType); if (source == SOURCE_SINK) { if (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("dequeueBuffer: no buffer, bailing out"); return NO_MEMORY; } // We already dequeued the output buffer. If the GLES driver wants // something incompatible, we have to cancel and get a new one. This // will mean that HWC will see a different output buffer between // prepare and set, but since we're in GLES-only mode already it // shouldn't matter. usage |= GRALLOC_USAGE_HW_COMPOSER; const sp<GraphicBuffer>& buf = mProducerBuffers[mOutputProducerSlot]; if ((usage & ~buf->getUsage()) != 0 || (format != 0 && format != (uint32_t)buf->getPixelFormat()) || (w != 0 && w != mSinkBufferWidth) || (h != 0 && h != mSinkBufferHeight)) { VDS_LOGV("dequeueBuffer: dequeueing new output buffer: " "want %dx%d fmt=%d use=%#x, " "have %dx%d fmt=%d use=%#x", w, h, format, usage, mSinkBufferWidth, mSinkBufferHeight, buf->getPixelFormat(), buf->getUsage()); mOutputFormat = format; mOutputUsage = usage; result = refreshOutputBuffer(); if (result < 0) return result; } } if (source == SOURCE_SINK) { *pslot = mOutputProducerSlot; *fence = mOutputFence; } else { int sslot; result = dequeueBuffer(source, format, usage, &sslot, fence); if (result >= 0) { *pslot = mapSource2ProducerSlot(source, sslot); } } return result; }
VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t &hwcDisplayId, const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, const String8& name, bool secure) : ConsumerBase(bqConsumer), mHwc(hwc), mDisplayId(NO_MEMORY), mDisplayName(name), mOutputUsage(GRALLOC_USAGE_HW_COMPOSER), mProducerSlotSource(0), mDbgState(DBG_STATE_IDLE), mDbgLastCompositionType(COMPOSITION_UNKNOWN), mMustRecompose(false), mForceHwcCopy(false), mSecure(false) { mSource[SOURCE_SINK] = sink; mSource[SOURCE_SCRATCH] = bqProducer; int sinkWidth, sinkHeight, sinkFormat, sinkUsage; sink->query(NATIVE_WINDOW_WIDTH, &sinkWidth); sink->query(NATIVE_WINDOW_HEIGHT, &sinkHeight); sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat); sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage); mSinkBufferWidth = sinkWidth; mSinkBufferHeight = sinkHeight; // Pick the buffer format to request from the sink when not rendering to it // with GLES. If the consumer needs CPU access, use the default format // set by the consumer. Otherwise allow gralloc to decide the format based // on usage bits. mDefaultOutputFormat = sinkFormat; if((sinkUsage & GRALLOC_USAGE_HW_VIDEO_ENCODER) #ifdef QCOM_BSP && (sinkUsage & GRALLOC_USAGE_PRIVATE_WFD) #endif ) { mDefaultOutputFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; mForceHwcCopy = true; //Set secure flag only if the session requires HW protection, currently //there is no other way to distinguish different security protection levels //This allows Level-3 sessions(eg.simulated displayes) to get //buffers from IOMMU heap and not MM (secure) heap. mSecure = secure; } // XXX: With this debug property we can allow screenrecord to be composed // via HWC. This is useful for debugging purposes, for example when WFD // is not working on a particular build. char value[PROPERTY_VALUE_MAX]; if( (property_get("debug.hwc.screenrecord", value, NULL) > 0) && ((!strncmp(value, "1", strlen("1"))) || !strncasecmp(value, "true", strlen("true")))) { mForceHwcCopy = true; } // Once the mForceHwcCopy flag is set, we can freely allocate an HWC // display ID. if (mForceHwcCopy && mHwc.isVDSEnabled()) mDisplayId = mHwc.allocateDisplayId(); hwcDisplayId = mDisplayId; //update display id for device creation in SF mOutputFormat = mDefaultOutputFormat; // TODO: need to add the below logs as part of dumpsys output VDS_LOGV("creation: sinkFormat: 0x%x sinkUsage: 0x%x mForceHwcCopy: %d", mOutputFormat, sinkUsage, mForceHwcCopy); setOutputUsage(); resetPerFrameState(); ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string()); mConsumer->setConsumerName(ConsumerBase::mName); mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight); mConsumer->setDefaultMaxBufferCount(2); }