• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
dbgCompositionTypeStr(DisplaySurface::CompositionType type)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 
VirtualDisplaySurface(HWComposer & hwc,int32_t dispId,const sp<IGraphicBufferProducer> & sink,const sp<BufferQueue> & bq,const String8 & name)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 
~VirtualDisplaySurface()92 VirtualDisplaySurface::~VirtualDisplaySurface() {
93 }
94 
beginFrame()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 
prepareFrame(CompositionType compositionType)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 
compositionComplete()158 status_t VirtualDisplaySurface::compositionComplete() {
159     return NO_ERROR;
160 }
161 
advanceFrame()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 
onFrameCommitted()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 
dump(String8 & result) const247 void VirtualDisplaySurface::dump(String8& result) const {
248 }
249 
requestBuffer(int pslot,sp<GraphicBuffer> * outBuf)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 
setBufferCount(int bufferCount)260 status_t VirtualDisplaySurface::setBufferCount(int bufferCount) {
261     return mSource[SOURCE_SINK]->setBufferCount(bufferCount);
262 }
263 
dequeueBuffer(Source source,uint32_t format,uint32_t usage,int * sslot,sp<Fence> * fence)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 
dequeueBuffer(int * pslot,sp<Fence> * fence,bool async,uint32_t w,uint32_t h,uint32_t format,uint32_t usage)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 
queueBuffer(int pslot,const QueueBufferInput & input,QueueBufferOutput * output)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 
cancelBuffer(int pslot,const sp<Fence> & fence)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 
query(int what,int * value)430 int VirtualDisplaySurface::query(int what, int* value) {
431     return mSource[SOURCE_SINK]->query(what, value);
432 }
433 
connect(const sp<IBinder> & token,int api,bool producerControlledByApp,QueueBufferOutput * output)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 
disconnect(int api)446 status_t VirtualDisplaySurface::disconnect(int api) {
447     return mSource[SOURCE_SINK]->disconnect(api);
448 }
449 
updateQueueBufferOutput(const QueueBufferOutput & qbo)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 
resetPerFrameState()457 void VirtualDisplaySurface::resetPerFrameState() {
458     mCompositionType = COMPOSITION_UNKNOWN;
459     mSinkBufferWidth = 0;
460     mSinkBufferHeight = 0;
461     mOutputFence = Fence::NO_FENCE;
462     mOutputProducerSlot = -1;
463 }
464 
refreshOutputBuffer()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.
mapSource2ProducerSlot(Source source,int sslot)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 }
mapProducer2SourceSlot(Source source,int pslot)499 int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) {
500     return mapSource2ProducerSlot(source, pslot);
501 }
502 
503 VirtualDisplaySurface::Source
fbSourceForCompositionType(CompositionType type)504 VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
505     return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
506 }
507 
dbgStateStr() const508 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 
dbgSourceStr(Source s)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