Home | History | Annotate | Download | only in DisplayHardware
      1 /*
      2  * Copyright 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // #define LOG_NDEBUG 0
     18 #include "VirtualDisplaySurface.h"
     19 #include "HWComposer.h"
     20 
     21 // ---------------------------------------------------------------------------
     22 namespace android {
     23 // ---------------------------------------------------------------------------
     24 
     25 #if defined(FORCE_HWC_COPY_FOR_VIRTUAL_DISPLAYS)
     26 static const bool sForceHwcCopy = true;
     27 #else
     28 static const bool sForceHwcCopy = false;
     29 #endif
     30 
     31 #define VDS_LOGE(msg, ...) ALOGE("[%s] "msg, \
     32         mDisplayName.string(), ##__VA_ARGS__)
     33 #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] "msg, \
     34         mDisplayName.string(), ##__VA_ARGS__)
     35 #define VDS_LOGV(msg, ...) ALOGV("[%s] "msg, \
     36         mDisplayName.string(), ##__VA_ARGS__)
     37 
     38 static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
     39     switch (type) {
     40         case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
     41         case DisplaySurface::COMPOSITION_GLES:    return "GLES";
     42         case DisplaySurface::COMPOSITION_HWC:     return "HWC";
     43         case DisplaySurface::COMPOSITION_MIXED:   return "MIXED";
     44         default:                                  return "<INVALID>";
     45     }
     46 }
     47 
     48 VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId,
     49         const sp<IGraphicBufferProducer>& sink,
     50         const sp<BufferQueue>& bq,
     51         const String8& name)
     52 :   ConsumerBase(bq),
     53     mHwc(hwc),
     54     mDisplayId(dispId),
     55     mDisplayName(name),
     56     mOutputUsage(GRALLOC_USAGE_HW_COMPOSER),
     57     mProducerSlotSource(0),
     58     mDbgState(DBG_STATE_IDLE),
     59     mDbgLastCompositionType(COMPOSITION_UNKNOWN)
     60 {
     61     mSource[SOURCE_SINK] = sink;
     62     mSource[SOURCE_SCRATCH] = bq;
     63 
     64     resetPerFrameState();
     65 
     66     int sinkWidth, sinkHeight;
     67     sink->query(NATIVE_WINDOW_WIDTH, &sinkWidth);
     68     sink->query(NATIVE_WINDOW_HEIGHT, &sinkHeight);
     69 
     70     // Pick the buffer format to request from the sink when not rendering to it
     71     // with GLES. If the consumer needs CPU access, use the default format
     72     // set by the consumer. Otherwise allow gralloc to decide the format based
     73     // on usage bits.
     74     int sinkUsage;
     75     sink->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &sinkUsage);
     76     if (sinkUsage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) {
     77         int sinkFormat;
     78         sink->query(NATIVE_WINDOW_FORMAT, &sinkFormat);
     79         mDefaultOutputFormat = sinkFormat;
     80     } else {
     81         mDefaultOutputFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
     82     }
     83     mOutputFormat = mDefaultOutputFormat;
     84 
     85     ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string());
     86     mConsumer->setConsumerName(ConsumerBase::mName);
     87     mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
     88     mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight);
     89     mConsumer->setDefaultMaxBufferCount(2);
     90 }
     91 
     92 VirtualDisplaySurface::~VirtualDisplaySurface() {
     93 }
     94 
     95 status_t VirtualDisplaySurface::beginFrame() {
     96     if (mDisplayId < 0)
     97         return NO_ERROR;
     98 
     99     VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
    100             "Unexpected beginFrame() in %s state", dbgStateStr());
    101     mDbgState = DBG_STATE_BEGUN;
    102 
    103     uint32_t transformHint, numPendingBuffers;
    104     mQueueBufferOutput.deflate(&mSinkBufferWidth, &mSinkBufferHeight,
    105             &transformHint, &numPendingBuffers);
    106 
    107     return refreshOutputBuffer();
    108 }
    109 
    110 status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
    111     if (mDisplayId < 0)
    112         return NO_ERROR;
    113 
    114     VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
    115             "Unexpected prepareFrame() in %s state", dbgStateStr());
    116     mDbgState = DBG_STATE_PREPARED;
    117 
    118     mCompositionType = compositionType;
    119     if (sForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
    120         // Some hardware can do RGB->YUV conversion more efficiently in hardware
    121         // controlled by HWC than in hardware controlled by the video encoder.
    122         // Forcing GLES-composed frames to go through an extra copy by the HWC
    123         // allows the format conversion to happen there, rather than passing RGB
    124         // directly to the consumer.
    125         //
    126         // On the other hand, when the consumer prefers RGB or can consume RGB
    127         // inexpensively, this forces an unnecessary copy.
    128         mCompositionType = COMPOSITION_MIXED;
    129     }
    130 
    131     if (mCompositionType != mDbgLastCompositionType) {
    132         VDS_LOGV("prepareFrame: composition type changed to %s",
    133                 dbgCompositionTypeStr(mCompositionType));
    134         mDbgLastCompositionType = mCompositionType;
    135     }
    136 
    137     if (mCompositionType != COMPOSITION_GLES &&
    138             (mOutputFormat != mDefaultOutputFormat ||
    139              mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
    140         // We must have just switched from GLES-only to MIXED or HWC
    141         // composition. Stop using the format and usage requested by the GLES
    142         // driver; they may be suboptimal when HWC is writing to the output
    143         // buffer. For example, if the output is going to a video encoder, and
    144         // HWC can write directly to YUV, some hardware can skip a
    145         // memory-to-memory RGB-to-YUV conversion step.
    146         //
    147         // If we just switched *to* GLES-only mode, we'll change the
    148         // format/usage and get a new buffer when the GLES driver calls
    149         // dequeueBuffer().
    150         mOutputFormat = mDefaultOutputFormat;
    151         mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
    152         refreshOutputBuffer();
    153     }
    154 
    155     return NO_ERROR;
    156 }
    157 
    158 status_t VirtualDisplaySurface::compositionComplete() {
    159     return NO_ERROR;
    160 }
    161 
    162 status_t VirtualDisplaySurface::advanceFrame() {
    163     if (mDisplayId < 0)
    164         return NO_ERROR;
    165 
    166     if (mCompositionType == COMPOSITION_HWC) {
    167         VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
    168                 "Unexpected advanceFrame() in %s state on HWC frame",
    169                 dbgStateStr());
    170     } else {
    171         VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
    172                 "Unexpected advanceFrame() in %s state on GLES/MIXED frame",
    173                 dbgStateStr());
    174     }
    175     mDbgState = DBG_STATE_HWC;
    176 
    177     if (mOutputProducerSlot < 0 ||
    178             (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
    179         // Last chance bailout if something bad happened earlier. For example,
    180         // in a GLES configuration, if the sink disappears then dequeueBuffer
    181         // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
    182         // will soldier on. So we end up here without a buffer. There should
    183         // be lots of scary messages in the log just before this.
    184         VDS_LOGE("advanceFrame: no buffer, bailing out");
    185         return NO_MEMORY;
    186     }
    187 
    188     sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
    189             mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(NULL);
    190     sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
    191     VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
    192             mFbProducerSlot, fbBuffer.get(),
    193             mOutputProducerSlot, outBuffer.get());
    194 
    195     // At this point we know the output buffer acquire fence,
    196     // so update HWC state with it.
    197     mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
    198 
    199     status_t result = NO_ERROR;
    200     if (fbBuffer != NULL) {
    201         result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
    202     }
    203 
    204     return result;
    205 }
    206 
    207 void VirtualDisplaySurface::onFrameCommitted() {
    208     if (mDisplayId < 0)
    209         return;
    210 
    211     VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
    212             "Unexpected onFrameCommitted() in %s state", dbgStateStr());
    213     mDbgState = DBG_STATE_IDLE;
    214 
    215     sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
    216     if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
    217         // release the scratch buffer back to the pool
    218         Mutex::Autolock lock(mMutex);
    219         int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
    220         VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
    221         addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence);
    222         releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot],
    223                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
    224     }
    225 
    226     if (mOutputProducerSlot >= 0) {
    227         int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
    228         QueueBufferOutput qbo;
    229         sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId);
    230         VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
    231         status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
    232                 QueueBufferInput(
    233                     systemTime(), false /* isAutoTimestamp */,
    234                     Rect(mSinkBufferWidth, mSinkBufferHeight),
    235                     NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */,
    236                     true /* async*/,
    237                     outFence),
    238                 &qbo);
    239         if (result == NO_ERROR) {
    240             updateQueueBufferOutput(qbo);
    241         }
    242     }
    243 
    244     resetPerFrameState();
    245 }
    246 
    247 void VirtualDisplaySurface::dump(String8& result) const {
    248 }
    249 
    250 status_t VirtualDisplaySurface::requestBuffer(int pslot,
    251         sp<GraphicBuffer>* outBuf) {
    252     VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
    253             "Unexpected requestBuffer pslot=%d in %s state",
    254             pslot, dbgStateStr());
    255 
    256     *outBuf = mProducerBuffers[pslot];
    257     return NO_ERROR;
    258 }
    259 
    260 status_t VirtualDisplaySurface::setBufferCount(int bufferCount) {
    261     return mSource[SOURCE_SINK]->setBufferCount(bufferCount);
    262 }
    263 
    264 status_t VirtualDisplaySurface::dequeueBuffer(Source source,
    265         uint32_t format, uint32_t usage, int* sslot, sp<Fence>* fence) {
    266     // Don't let a slow consumer block us
    267     bool async = (source == SOURCE_SINK);
    268 
    269     status_t result = mSource[source]->dequeueBuffer(sslot, fence, async,
    270             mSinkBufferWidth, mSinkBufferHeight, format, usage);
    271     if (result < 0)
    272         return result;
    273     int pslot = mapSource2ProducerSlot(source, *sslot);
    274     VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
    275             dbgSourceStr(source), *sslot, pslot, result);
    276     uint32_t sourceBit = static_cast<uint32_t>(source) << pslot;
    277 
    278     if ((mProducerSlotSource & (1u << pslot)) != sourceBit) {
    279         // This slot was previously dequeued from the other source; must
    280         // re-request the buffer.
    281         result |= BUFFER_NEEDS_REALLOCATION;
    282         mProducerSlotSource &= ~(1u << pslot);
    283         mProducerSlotSource |= sourceBit;
    284     }
    285 
    286     if (result & RELEASE_ALL_BUFFERS) {
    287         for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
    288             if ((mProducerSlotSource & (1u << i)) == sourceBit)
    289                 mProducerBuffers[i].clear();
    290         }
    291     }
    292     if (result & BUFFER_NEEDS_REALLOCATION) {
    293         mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]);
    294         VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#x",
    295                 dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
    296                 mProducerBuffers[pslot]->getPixelFormat(),
    297                 mProducerBuffers[pslot]->getUsage());
    298     }
    299 
    300     return result;
    301 }
    302 
    303 status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool async,
    304         uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
    305     VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
    306             "Unexpected dequeueBuffer() in %s state", dbgStateStr());
    307     mDbgState = DBG_STATE_GLES;
    308 
    309     VDS_LOGW_IF(!async, "EGL called dequeueBuffer with !async despite eglSwapInterval(0)");
    310     VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage);
    311 
    312     status_t result = NO_ERROR;
    313     Source source = fbSourceForCompositionType(mCompositionType);
    314 
    315     if (source == SOURCE_SINK) {
    316 
    317         if (mOutputProducerSlot < 0) {
    318             // Last chance bailout if something bad happened earlier. For example,
    319             // in a GLES configuration, if the sink disappears then dequeueBuffer
    320             // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
    321             // will soldier on. So we end up here without a buffer. There should
    322             // be lots of scary messages in the log just before this.
    323             VDS_LOGE("dequeueBuffer: no buffer, bailing out");
    324             return NO_MEMORY;
    325         }
    326 
    327         // We already dequeued the output buffer. If the GLES driver wants
    328         // something incompatible, we have to cancel and get a new one. This
    329         // will mean that HWC will see a different output buffer between
    330         // prepare and set, but since we're in GLES-only mode already it
    331         // shouldn't matter.
    332 
    333         usage |= GRALLOC_USAGE_HW_COMPOSER;
    334         const sp<GraphicBuffer>& buf = mProducerBuffers[mOutputProducerSlot];
    335         if ((usage & ~buf->getUsage()) != 0 ||
    336                 (format != 0 && format != (uint32_t)buf->getPixelFormat()) ||
    337                 (w != 0 && w != mSinkBufferWidth) ||
    338                 (h != 0 && h != mSinkBufferHeight)) {
    339             VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
    340                     "want %dx%d fmt=%d use=%#x, "
    341                     "have %dx%d fmt=%d use=%#x",
    342                     w, h, format, usage,
    343                     mSinkBufferWidth, mSinkBufferHeight,
    344                     buf->getPixelFormat(), buf->getUsage());
    345             mOutputFormat = format;
    346             mOutputUsage = usage;
    347             result = refreshOutputBuffer();
    348             if (result < 0)
    349                 return result;
    350         }
    351     }
    352 
    353     if (source == SOURCE_SINK) {
    354         *pslot = mOutputProducerSlot;
    355         *fence = mOutputFence;
    356     } else {
    357         int sslot;
    358         result = dequeueBuffer(source, format, usage, &sslot, fence);
    359         if (result >= 0) {
    360             *pslot = mapSource2ProducerSlot(source, sslot);
    361         }
    362     }
    363     return result;
    364 }
    365 
    366 status_t VirtualDisplaySurface::queueBuffer(int pslot,
    367         const QueueBufferInput& input, QueueBufferOutput* output) {
    368     VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
    369             "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
    370             dbgStateStr());
    371     mDbgState = DBG_STATE_GLES_DONE;
    372 
    373     VDS_LOGV("queueBuffer pslot=%d", pslot);
    374 
    375     status_t result;
    376     if (mCompositionType == COMPOSITION_MIXED) {
    377         // Queue the buffer back into the scratch pool
    378         QueueBufferOutput scratchQBO;
    379         int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
    380         result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO);
    381         if (result != NO_ERROR)
    382             return result;
    383 
    384         // Now acquire the buffer from the scratch pool -- should be the same
    385         // slot and fence as we just queued.
    386         Mutex::Autolock lock(mMutex);
    387         BufferQueue::BufferItem item;
    388         result = acquireBufferLocked(&item, 0);
    389         if (result != NO_ERROR)
    390             return result;
    391         VDS_LOGW_IF(item.mBuf != sslot,
    392                 "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
    393                 item.mBuf, sslot);
    394         mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf);
    395         mFbFence = mSlots[item.mBuf].mFence;
    396 
    397     } else {
    398         LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
    399                 "Unexpected queueBuffer in state %s for compositionType %s",
    400                 dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
    401 
    402         // Extract the GLES release fence for HWC to acquire
    403         int64_t timestamp;
    404         bool isAutoTimestamp;
    405         Rect crop;
    406         int scalingMode;
    407         uint32_t transform;
    408         bool async;
    409         input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode,
    410                 &transform, &async, &mFbFence);
    411 
    412         mFbProducerSlot = pslot;
    413         mOutputFence = mFbFence;
    414     }
    415 
    416     *output = mQueueBufferOutput;
    417     return NO_ERROR;
    418 }
    419 
    420 void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) {
    421     VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
    422             "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
    423             dbgStateStr());
    424     VDS_LOGV("cancelBuffer pslot=%d", pslot);
    425     Source source = fbSourceForCompositionType(mCompositionType);
    426     return mSource[source]->cancelBuffer(
    427             mapProducer2SourceSlot(source, pslot), fence);
    428 }
    429 
    430 int VirtualDisplaySurface::query(int what, int* value) {
    431     return mSource[SOURCE_SINK]->query(what, value);
    432 }
    433 
    434 status_t VirtualDisplaySurface::connect(const sp<IBinder>& token,
    435         int api, bool producerControlledByApp,
    436         QueueBufferOutput* output) {
    437     QueueBufferOutput qbo;
    438     status_t result = mSource[SOURCE_SINK]->connect(token, api, producerControlledByApp, &qbo);
    439     if (result == NO_ERROR) {
    440         updateQueueBufferOutput(qbo);
    441         *output = mQueueBufferOutput;
    442     }
    443     return result;
    444 }
    445 
    446 status_t VirtualDisplaySurface::disconnect(int api) {
    447     return mSource[SOURCE_SINK]->disconnect(api);
    448 }
    449 
    450 void VirtualDisplaySurface::updateQueueBufferOutput(
    451         const QueueBufferOutput& qbo) {
    452     uint32_t w, h, transformHint, numPendingBuffers;
    453     qbo.deflate(&w, &h, &transformHint, &numPendingBuffers);
    454     mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers);
    455 }
    456 
    457 void VirtualDisplaySurface::resetPerFrameState() {
    458     mCompositionType = COMPOSITION_UNKNOWN;
    459     mSinkBufferWidth = 0;
    460     mSinkBufferHeight = 0;
    461     mOutputFence = Fence::NO_FENCE;
    462     mOutputProducerSlot = -1;
    463 }
    464 
    465 status_t VirtualDisplaySurface::refreshOutputBuffer() {
    466     if (mOutputProducerSlot >= 0) {
    467         mSource[SOURCE_SINK]->cancelBuffer(
    468                 mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot),
    469                 mOutputFence);
    470     }
    471 
    472     int sslot;
    473     status_t result = dequeueBuffer(SOURCE_SINK, mOutputFormat, mOutputUsage,
    474             &sslot, &mOutputFence);
    475     if (result < 0)
    476         return result;
    477     mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
    478 
    479     // On GLES-only frames, we don't have the right output buffer acquire fence
    480     // until after GLES calls queueBuffer(). So here we just set the buffer
    481     // (for use in HWC prepare) but not the fence; we'll call this again with
    482     // the proper fence once we have it.
    483     result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
    484             mProducerBuffers[mOutputProducerSlot]);
    485 
    486     return result;
    487 }
    488 
    489 // This slot mapping function is its own inverse, so two copies are unnecessary.
    490 // Both are kept to make the intent clear where the function is called, and for
    491 // the (unlikely) chance that we switch to a different mapping function.
    492 int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) {
    493     if (source == SOURCE_SCRATCH) {
    494         return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1;
    495     } else {
    496         return sslot;
    497     }
    498 }
    499 int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) {
    500     return mapSource2ProducerSlot(source, pslot);
    501 }
    502 
    503 VirtualDisplaySurface::Source
    504 VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
    505     return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
    506 }
    507 
    508 const char* VirtualDisplaySurface::dbgStateStr() const {
    509     switch (mDbgState) {
    510         case DBG_STATE_IDLE:      return "IDLE";
    511         case DBG_STATE_PREPARED:  return "PREPARED";
    512         case DBG_STATE_GLES:      return "GLES";
    513         case DBG_STATE_GLES_DONE: return "GLES_DONE";
    514         case DBG_STATE_HWC:       return "HWC";
    515         default:                  return "INVALID";
    516     }
    517 }
    518 
    519 const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
    520     switch (s) {
    521         case SOURCE_SINK:    return "SINK";
    522         case SOURCE_SCRATCH: return "SCRATCH";
    523         default:             return "INVALID";
    524     }
    525 }
    526 
    527 // ---------------------------------------------------------------------------
    528 } // namespace android
    529 // ---------------------------------------------------------------------------
    530