/* * 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. */ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" // #define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include "HWComposer.h" #include "SurfaceFlinger.h" #include "VirtualDisplaySurface.h" #define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \ mDisplayName.c_str(), ##__VA_ARGS__) #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \ mDisplayName.c_str(), ##__VA_ARGS__) #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \ mDisplayName.c_str(), ##__VA_ARGS__) #define UNSUPPORTED() \ VDS_LOGE("%s: Invalid operation on virtual display", __func__); \ return INVALID_OPERATION namespace android { VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId, const sp& sink, const sp& bqProducer, const sp& bqConsumer, const std::string& name) #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) : ConsumerBase(bqProducer, bqConsumer), #else : ConsumerBase(bqConsumer), #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) mHwc(hwc), mDisplayId(displayId), mDisplayName(name), mSource{}, mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), mOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), mOutputUsage(GRALLOC_USAGE_HW_COMPOSER), mProducerSlotSource(0), mProducerBuffers(), mProducerSlotNeedReallocation(0), mQueueBufferOutput(), mSinkBufferWidth(0), mSinkBufferHeight(0), mFbFence(Fence::NO_FENCE), mOutputFence(Fence::NO_FENCE), mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT), mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT), mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) { mSource[SOURCE_SINK] = sink; mSource[SOURCE_SCRATCH] = bqProducer; resetPerFrameState(); int sinkWidth, sinkHeight; sink->query(NATIVE_WINDOW_WIDTH, &sinkWidth); sink->query(NATIVE_WINDOW_HEIGHT, &sinkHeight); mSinkBufferWidth = sinkWidth; mSinkBufferHeight = sinkHeight; // Pick the buffer format to request from the sink when not rendering to it // with GPU. 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. int sinkUsage; sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage); if (sinkUsage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) { int sinkFormat; sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat); mDefaultOutputFormat = sinkFormat; } else { mDefaultOutputFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; } mOutputFormat = mDefaultOutputFormat; ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.c_str()); mConsumer->setConsumerName(ConsumerBase::mName); mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight); sink->setAsyncMode(true); IGraphicBufferProducer::QueueBufferOutput output; mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output); for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) { mHwcBufferIds[i] = UINT64_MAX; } } VirtualDisplaySurface::~VirtualDisplaySurface() { mSource[SOURCE_SCRATCH]->disconnect(NATIVE_WINDOW_API_EGL); } status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return NO_ERROR; } mMustRecompose = mustRecompose; VDS_LOGW_IF(mDebugState != DebugState::Idle, "Unexpected %s in %s state", __func__, ftl::enum_string(mDebugState).c_str()); mDebugState = DebugState::Begun; return refreshOutputBuffer(); } status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return NO_ERROR; } VDS_LOGW_IF(mDebugState != DebugState::Begun, "Unexpected %s in %s state", __func__, ftl::enum_string(mDebugState).c_str()); mDebugState = DebugState::Prepared; mCompositionType = compositionType; if (mForceHwcCopy && mCompositionType == CompositionType::Gpu) { // Some hardware can do RGB->YUV conversion more efficiently in hardware // controlled by HWC than in hardware controlled by the video encoder. // Forcing GPU-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 = CompositionType::Mixed; } if (mCompositionType != mDebugLastCompositionType) { VDS_LOGV("%s: composition type changed to %s", __func__, toString(mCompositionType).c_str()); mDebugLastCompositionType = mCompositionType; } if (mCompositionType != CompositionType::Gpu && (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) { // We must have just switched from GPU-only to MIXED or HWC // composition. Stop using the format and usage requested by the GPU // 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* GPU-only mode, we'll change the // format/usage and get a new buffer when the GPU driver calls // dequeueBuffer(). mOutputFormat = mDefaultOutputFormat; mOutputUsage = GRALLOC_USAGE_HW_COMPOSER; refreshOutputBuffer(); } return NO_ERROR; } status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return NO_ERROR; } if (mCompositionType == CompositionType::Hwc) { VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state on HWC frame", __func__, ftl::enum_string(mDebugState).c_str()); } else { VDS_LOGW_IF(mDebugState != DebugState::GpuDone, "Unexpected %s in %s state on GPU/MIXED frame", __func__, ftl::enum_string(mDebugState).c_str()); } mDebugState = DebugState::Hwc; if (mOutputProducerSlot < 0 || (mCompositionType != CompositionType::Hwc && mFbProducerSlot < 0)) { // Last chance bailout if something bad happened earlier. For example, // in a graphics API configuration, if the sink disappears then dequeueBuffer // will fail, the GPU 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("%s: no buffer, bailing out", __func__); return NO_MEMORY; } sp const& fbBuffer = mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp(nullptr); sp const& outBuffer = mProducerBuffers[mOutputProducerSlot]; VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(), mOutputProducerSlot, outBuffer.get()); const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); LOG_FATAL_IF(!halDisplayId); // At this point we know the output buffer acquire fence, // so update HWC state with it. mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer); status_t result = NO_ERROR; if (fbBuffer != nullptr) { // assume that HWC has previously seen the buffer in this slot sp hwcBuffer = sp(nullptr); if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) { mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId(); hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot } // TODO: Correctly propagate the dataspace from GL composition result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer, ui::Dataspace::UNKNOWN, hdrSdrRatio); } return result; } void VirtualDisplaySurface::onFrameCommitted() { const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); if (!halDisplayId) { return; } VDS_LOGW_IF(mDebugState != DebugState::Hwc, "Unexpected %s in %s state", __func__, ftl::enum_string(mDebugState).c_str()); mDebugState = DebugState::Idle; sp retireFence = mHwc.getPresentFence(*halDisplayId); if (mCompositionType == CompositionType::Mixed && mFbProducerSlot >= 0) { // release the scratch buffer back to the pool Mutex::Autolock lock(mMutex); int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot); addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], retireFence); releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]); } if (mOutputProducerSlot >= 0) { int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot); QueueBufferOutput qbo; VDS_LOGV("%s: queue sink sslot=%d", __func__, sslot); if (mMustRecompose) { status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, QueueBufferInput( systemTime(), false /* isAutoTimestamp */, HAL_DATASPACE_UNKNOWN, Rect(mSinkBufferWidth, mSinkBufferHeight), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */, retireFence), &qbo); if (result == NO_ERROR) { updateQueueBufferOutput(std::move(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, retireFence); } } resetPerFrameState(); } void VirtualDisplaySurface::dumpAsString(String8& /* result */) const { } void VirtualDisplaySurface::resizeBuffers(const ui::Size& newSize) { mQueueBufferOutput.width = newSize.width; mQueueBufferOutput.height = newSize.height; mSinkBufferWidth = newSize.width; mSinkBufferHeight = newSize.height; } const sp& VirtualDisplaySurface::getClientTargetAcquireFence() const { return mFbFence; } status_t VirtualDisplaySurface::requestBuffer(int pslot, sp* outBuf) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf); } VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s pslot=%d in %s state", __func__, pslot, ftl::enum_string(mDebugState).c_str()); *outBuf = mProducerBuffers[pslot]; return NO_ERROR; } status_t VirtualDisplaySurface::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { return mSource[SOURCE_SINK]->setMaxDequeuedBufferCount(maxDequeuedBuffers); } status_t VirtualDisplaySurface::setAsyncMode(bool async) { return mSource[SOURCE_SINK]->setAsyncMode(async); } status_t VirtualDisplaySurface::dequeueBuffer(Source source, PixelFormat format, uint64_t usage, int* sslot, sp* fence) { LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); status_t result = mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight, format, usage, nullptr, nullptr); if (result < 0) return result; int pslot = mapSource2ProducerSlot(source, *sslot); VDS_LOGV("%s(%s): sslot=%d pslot=%d result=%d", __func__, ftl::enum_string(source).c_str(), *sslot, pslot, result); uint64_t sourceBit = static_cast(source) << pslot; // reset producer slot reallocation flag mProducerSlotNeedReallocation &= ~(1ULL << pslot); if ((mProducerSlotSource & (1ULL << pslot)) != sourceBit) { // This slot was previously dequeued from the other source; must // re-request the buffer. mProducerSlotNeedReallocation |= 1ULL << pslot; 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("%s(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, __func__, ftl::enum_string(source).c_str(), pslot, mProducerBuffers[pslot].get(), mProducerBuffers[pslot]->getPixelFormat(), mProducerBuffers[pslot]->getUsage()); // propagate reallocation to VDS consumer mProducerSlotNeedReallocation |= 1ULL << pslot; } return result; } status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp* fence, uint32_t w, uint32_t h, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge, outTimestamps); } VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state", __func__, ftl::enum_string(mDebugState).c_str()); mDebugState = DebugState::Gpu; VDS_LOGV("%s %dx%d fmt=%d usage=%#" PRIx64, __func__, 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 graphics API configuration, if the sink disappears then dequeueBuffer // will fail, the GPU 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("%s: no buffer, bailing out", __func__); return NO_MEMORY; } // We already dequeued the output buffer. If the GPU 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 GPU-only mode already it // shouldn't matter. usage |= GRALLOC_USAGE_HW_COMPOSER; const sp& buf = mProducerBuffers[mOutputProducerSlot]; if ((usage & ~buf->getUsage()) != 0 || (format != 0 && format != buf->getPixelFormat()) || (w != 0 && w != mSinkBufferWidth) || (h != 0 && h != mSinkBufferHeight)) { VDS_LOGV("%s: dequeueing new output buffer: " "want %dx%d fmt=%d use=%#" PRIx64 ", " "have %dx%d fmt=%d use=%#" PRIx64, __func__, 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); } } if (outBufferAge) { *outBufferAge = 0; } if ((mProducerSlotNeedReallocation & (1ULL << *pslot)) != 0) { result |= BUFFER_NEEDS_REALLOCATION; } return result; } status_t VirtualDisplaySurface::detachBuffer(int) { UNSUPPORTED(); } status_t VirtualDisplaySurface::detachNextBuffer(sp*, sp*) { UNSUPPORTED(); } status_t VirtualDisplaySurface::attachBuffer(int*, const sp&) { UNSUPPORTED(); } status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output); } VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__, pslot, ftl::enum_string(mDebugState).c_str()); mDebugState = DebugState::GpuDone; VDS_LOGV("%s pslot=%d", __func__, pslot); status_t result; if (mCompositionType == CompositionType::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.mSlot != sslot, "%s: acquired sslot %d from SCRATCH after queueing sslot %d", __func__, item.mSlot, sslot); mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mSlot); mFbFence = mSlots[item.mSlot].mFence; } else { LOG_FATAL_IF(mCompositionType != CompositionType::Gpu, "Unexpected %s in state %s for composition type %s", __func__, ftl::enum_string(mDebugState).c_str(), toString(mCompositionType).c_str()); // Extract the GPU release fence for HWC to acquire int64_t timestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop; int scalingMode; uint32_t transform; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &mFbFence); mFbProducerSlot = pslot; mOutputFence = mFbFence; } // This moves the frame timestamps and keeps a copy of all other fields. *output = std::move(mQueueBufferOutput); return NO_ERROR; } status_t VirtualDisplaySurface::cancelBuffer(int pslot, const sp& fence) { if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence); } VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__, pslot, ftl::enum_string(mDebugState).c_str()); VDS_LOGV("%s pslot=%d", __func__, pslot); Source source = fbSourceForCompositionType(mCompositionType); return mSource[source]->cancelBuffer( mapProducer2SourceSlot(source, pslot), fence); } int VirtualDisplaySurface::query(int what, int* value) { switch (what) { case NATIVE_WINDOW_WIDTH: *value = mSinkBufferWidth; break; case NATIVE_WINDOW_HEIGHT: *value = mSinkBufferHeight; break; default: return mSource[SOURCE_SINK]->query(what, value); } return NO_ERROR; } status_t VirtualDisplaySurface::connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { QueueBufferOutput qbo; status_t result = mSource[SOURCE_SINK]->connect(listener, api, producerControlledByApp, &qbo); if (result == NO_ERROR) { updateQueueBufferOutput(std::move(qbo)); // This moves the frame timestamps and keeps a copy of all other fields. *output = std::move(mQueueBufferOutput); } return result; } status_t VirtualDisplaySurface::disconnect(int api, DisconnectMode mode) { return mSource[SOURCE_SINK]->disconnect(api, mode); } status_t VirtualDisplaySurface::setSidebandStream(const sp&) { UNSUPPORTED(); } void VirtualDisplaySurface::allocateBuffers(uint32_t /* width */, uint32_t /* height */, PixelFormat /* format */, uint64_t /* usage */) { // TODO: Should we actually allocate buffers for a virtual display? } status_t VirtualDisplaySurface::allowAllocation(bool /* allow */) { return INVALID_OPERATION; } status_t VirtualDisplaySurface::setGenerationNumber(uint32_t) { UNSUPPORTED(); } String8 VirtualDisplaySurface::getConsumerName() const { return String8("VirtualDisplaySurface"); } status_t VirtualDisplaySurface::setSharedBufferMode(bool) { UNSUPPORTED(); } status_t VirtualDisplaySurface::setAutoRefresh(bool) { UNSUPPORTED(); } status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t) { UNSUPPORTED(); } status_t VirtualDisplaySurface::getLastQueuedBuffer(sp*, sp*, float[16]) { UNSUPPORTED(); } status_t VirtualDisplaySurface::getUniqueId(uint64_t*) const { UNSUPPORTED(); } status_t VirtualDisplaySurface::getConsumerUsage(uint64_t* outUsage) const { return mSource[SOURCE_SINK]->getConsumerUsage(outUsage); } void VirtualDisplaySurface::updateQueueBufferOutput( QueueBufferOutput&& qbo) { mQueueBufferOutput = std::move(qbo); mQueueBufferOutput.transformHint = 0; } void VirtualDisplaySurface::resetPerFrameState() { mCompositionType = CompositionType::Unknown; mFbFence = Fence::NO_FENCE; mOutputFence = Fence::NO_FENCE; mOutputProducerSlot = -1; mFbProducerSlot = -1; } status_t VirtualDisplaySurface::refreshOutputBuffer() { LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); if (mOutputProducerSlot >= 0) { mSource[SOURCE_SINK]->cancelBuffer( mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot), mOutputFence); } int sslot; status_t result = dequeueBuffer(SOURCE_SINK, mOutputFormat, mOutputUsage, &sslot, &mOutputFence); if (result < 0) return result; mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot); // On GPU-only frames, we don't have the right output buffer acquire fence // until after GPU calls queueBuffer(). So here we just set the buffer // (for use in HWC prepare) but not the fence; we'll call this again with // the proper fence once we have it. const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); LOG_FATAL_IF(!halDisplayId); result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE, mProducerBuffers[mOutputProducerSlot]); return result; } // 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); } auto VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) -> Source { return type == CompositionType::Mixed ? SOURCE_SCRATCH : SOURCE_SINK; } std::string VirtualDisplaySurface::toString(CompositionType type) { using namespace std::literals; return type == CompositionType::Unknown ? "Unknown"s : ftl::Flags(type).string(); } } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion"