Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 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 | |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 17 | // #define LOG_NDEBUG 0 |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 18 | #include "VirtualDisplaySurface.h" |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 19 | #include "HWComposer.h" |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 20 | |
| 21 | // --------------------------------------------------------------------------- |
| 22 | namespace android { |
| 23 | // --------------------------------------------------------------------------- |
| 24 | |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 25 | #define VDS_LOGE(msg, ...) ALOGE("[%s] "msg, \ |
| 26 | mDisplayName.string(), ##__VA_ARGS__) |
| 27 | #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] "msg, \ |
| 28 | mDisplayName.string(), ##__VA_ARGS__) |
| 29 | #define VDS_LOGV(msg, ...) ALOGV("[%s] "msg, \ |
| 30 | mDisplayName.string(), ##__VA_ARGS__) |
| 31 | |
| 32 | static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) { |
| 33 | switch (type) { |
| 34 | case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN"; |
| 35 | case DisplaySurface::COMPOSITION_GLES: return "GLES"; |
| 36 | case DisplaySurface::COMPOSITION_HWC: return "HWC"; |
| 37 | case DisplaySurface::COMPOSITION_MIXED: return "MIXED"; |
| 38 | default: return "<INVALID>"; |
| 39 | } |
| 40 | } |
| 41 | |
Jesse Hall | ffe1f19 | 2013-03-22 15:13:48 -0700 | [diff] [blame] | 42 | VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 43 | const sp<IGraphicBufferProducer>& sink, const String8& name) |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 44 | : ConsumerBase(new BufferQueue(true)), |
| 45 | mHwc(hwc), |
| 46 | mDisplayId(dispId), |
| 47 | mDisplayName(name), |
| 48 | mProducerUsage(GRALLOC_USAGE_HW_COMPOSER), |
| 49 | mProducerSlotSource(0), |
| 50 | mDbgState(DBG_STATE_IDLE), |
| 51 | mDbgLastCompositionType(COMPOSITION_UNKNOWN) |
Jesse Hall | ffe1f19 | 2013-03-22 15:13:48 -0700 | [diff] [blame] | 52 | { |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 53 | mSource[SOURCE_SINK] = sink; |
| 54 | mSource[SOURCE_SCRATCH] = mBufferQueue; |
| 55 | |
| 56 | resetPerFrameState(); |
| 57 | |
| 58 | int sinkWidth, sinkHeight; |
| 59 | mSource[SOURCE_SINK]->query(NATIVE_WINDOW_WIDTH, &sinkWidth); |
| 60 | mSource[SOURCE_SINK]->query(NATIVE_WINDOW_HEIGHT, &sinkHeight); |
| 61 | |
| 62 | ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string()); |
| 63 | mBufferQueue->setConsumerName(ConsumerBase::mName); |
| 64 | mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); |
| 65 | mBufferQueue->setDefaultBufferSize(sinkWidth, sinkHeight); |
| 66 | mBufferQueue->setDefaultMaxBufferCount(2); |
Jesse Hall | ffe1f19 | 2013-03-22 15:13:48 -0700 | [diff] [blame] | 67 | } |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 68 | |
| 69 | VirtualDisplaySurface::~VirtualDisplaySurface() { |
| 70 | } |
| 71 | |
| 72 | sp<IGraphicBufferProducer> VirtualDisplaySurface::getIGraphicBufferProducer() const { |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 73 | if (mDisplayId >= 0) { |
| 74 | return static_cast<IGraphicBufferProducer*>( |
| 75 | const_cast<VirtualDisplaySurface*>(this)); |
| 76 | } else { |
| 77 | // There won't be any interaction with HWC for this virtual display, |
| 78 | // so the GLES driver can pass buffers directly to the sink. |
| 79 | return mSource[SOURCE_SINK]; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { |
| 84 | if (mDisplayId < 0) |
| 85 | return NO_ERROR; |
| 86 | |
| 87 | VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE, |
| 88 | "Unexpected prepareFrame() in %s state", dbgStateStr()); |
| 89 | mDbgState = DBG_STATE_PREPARED; |
| 90 | |
| 91 | mCompositionType = compositionType; |
| 92 | |
| 93 | if (mCompositionType != mDbgLastCompositionType) { |
| 94 | VDS_LOGV("prepareFrame: composition type changed to %s", |
| 95 | dbgCompositionTypeStr(mCompositionType)); |
| 96 | mDbgLastCompositionType = mCompositionType; |
| 97 | } |
| 98 | |
| 99 | return NO_ERROR; |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | status_t VirtualDisplaySurface::compositionComplete() { |
| 103 | return NO_ERROR; |
| 104 | } |
| 105 | |
| 106 | status_t VirtualDisplaySurface::advanceFrame() { |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 107 | if (mDisplayId < 0) |
| 108 | return NO_ERROR; |
| 109 | |
| 110 | if (mCompositionType == COMPOSITION_HWC) { |
| 111 | VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, |
| 112 | "Unexpected advanceFrame() in %s state on HWC frame", |
| 113 | dbgStateStr()); |
| 114 | } else { |
| 115 | VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE, |
| 116 | "Unexpected advanceFrame() in %s state on GLES/MIXED frame", |
| 117 | dbgStateStr()); |
| 118 | } |
| 119 | mDbgState = DBG_STATE_HWC; |
| 120 | |
| 121 | status_t result; |
| 122 | sp<Fence> outFence; |
| 123 | if (mCompositionType != COMPOSITION_GLES) { |
| 124 | // Dequeue an output buffer from the sink |
| 125 | uint32_t transformHint, numPendingBuffers; |
| 126 | mQueueBufferOutput.deflate(&mSinkBufferWidth, &mSinkBufferHeight, |
| 127 | &transformHint, &numPendingBuffers); |
| 128 | int sslot; |
| 129 | result = dequeueBuffer(SOURCE_SINK, 0, &sslot, &outFence); |
| 130 | if (result < 0) |
| 131 | return result; |
| 132 | mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot); |
| 133 | } |
| 134 | |
| 135 | if (mCompositionType == COMPOSITION_HWC) { |
| 136 | // We just dequeued the output buffer, use it for FB as well |
| 137 | mFbProducerSlot = mOutputProducerSlot; |
| 138 | mFbFence = outFence; |
| 139 | } else if (mCompositionType == COMPOSITION_GLES) { |
| 140 | mOutputProducerSlot = mFbProducerSlot; |
| 141 | outFence = mFbFence; |
| 142 | } else { |
| 143 | // mFbFence and mFbProducerSlot were set in queueBuffer, |
| 144 | // and mOutputProducerSlot and outFence were set above when dequeueing |
| 145 | // the sink buffer. |
| 146 | } |
| 147 | |
| 148 | if (mFbProducerSlot < 0 || mOutputProducerSlot < 0) { |
| 149 | // Last chance bailout if something bad happened earlier. For example, |
| 150 | // in a GLES configuration, if the sink disappears then dequeueBuffer |
| 151 | // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger |
| 152 | // will soldier on. So we end up here without a buffer. There should |
| 153 | // be lots of scary messages in the log just before this. |
| 154 | VDS_LOGE("advanceFrame: no buffer, bailing out"); |
| 155 | return NO_MEMORY; |
| 156 | } |
| 157 | |
| 158 | sp<GraphicBuffer> fbBuffer = mProducerBuffers[mFbProducerSlot]; |
| 159 | sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot]; |
| 160 | VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)", |
| 161 | mFbProducerSlot, fbBuffer.get(), |
| 162 | mOutputProducerSlot, outBuffer.get()); |
| 163 | |
| 164 | result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer); |
| 165 | if (result == NO_ERROR) { |
| 166 | result = mHwc.setOutputBuffer(mDisplayId, outFence, outBuffer); |
| 167 | } |
| 168 | |
| 169 | return result; |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 170 | } |
| 171 | |
Jesse Hall | 851cfe8 | 2013-03-20 13:44:00 -0700 | [diff] [blame] | 172 | void VirtualDisplaySurface::onFrameCommitted() { |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 173 | if (mDisplayId < 0) |
| 174 | return; |
| 175 | |
| 176 | VDS_LOGW_IF(mDbgState != DBG_STATE_HWC, |
| 177 | "Unexpected onFrameCommitted() in %s state", dbgStateStr()); |
| 178 | mDbgState = DBG_STATE_IDLE; |
| 179 | |
| 180 | sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId); |
| 181 | if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) { |
| 182 | // release the scratch buffer back to the pool |
| 183 | Mutex::Autolock lock(mMutex); |
| 184 | int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); |
| 185 | VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot); |
| 186 | addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence); |
| 187 | releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot], |
| 188 | EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); |
| 189 | } |
| 190 | |
| 191 | if (mOutputProducerSlot >= 0) { |
| 192 | int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot); |
| 193 | QueueBufferOutput qbo; |
| 194 | sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId); |
| 195 | VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot); |
| 196 | status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, |
| 197 | QueueBufferInput(systemTime(), |
| 198 | Rect(mSinkBufferWidth, mSinkBufferHeight), |
| 199 | NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, outFence), |
| 200 | &qbo); |
| 201 | if (result == NO_ERROR) { |
| 202 | updateQueueBufferOutput(qbo); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | resetPerFrameState(); |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | void VirtualDisplaySurface::dump(String8& result) const { |
| 210 | } |
| 211 | |
Jesse Hall | 38efe86 | 2013-04-06 23:12:29 -0700 | [diff] [blame^] | 212 | status_t VirtualDisplaySurface::requestBuffer(int pslot, |
| 213 | sp<GraphicBuffer>* outBuf) { |
| 214 | VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, |
| 215 | "Unexpected requestBuffer pslot=%d in %s state", |
| 216 | pslot, dbgStateStr()); |
| 217 | |
| 218 | *outBuf = mProducerBuffers[pslot]; |
| 219 | return NO_ERROR; |
| 220 | } |
| 221 | |
| 222 | status_t VirtualDisplaySurface::setBufferCount(int bufferCount) { |
| 223 | return mSource[SOURCE_SINK]->setBufferCount(bufferCount); |
| 224 | } |
| 225 | |
| 226 | status_t VirtualDisplaySurface::dequeueBuffer(Source source, |
| 227 | uint32_t format, int* sslot, sp<Fence>* fence) { |
| 228 | status_t result = mSource[source]->dequeueBuffer(sslot, fence, |
| 229 | mSinkBufferWidth, mSinkBufferHeight, format, mProducerUsage); |
| 230 | if (result < 0) |
| 231 | return result; |
| 232 | int pslot = mapSource2ProducerSlot(source, *sslot); |
| 233 | VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d", |
| 234 | dbgSourceStr(source), *sslot, pslot, result); |
| 235 | uint32_t sourceBit = static_cast<uint32_t>(source) << pslot; |
| 236 | |
| 237 | if ((mProducerSlotSource & (1u << pslot)) != sourceBit) { |
| 238 | // This slot was previously dequeued from the other source; must |
| 239 | // re-request the buffer. |
| 240 | result |= BUFFER_NEEDS_REALLOCATION; |
| 241 | mProducerSlotSource &= ~(1u << pslot); |
| 242 | mProducerSlotSource |= sourceBit; |
| 243 | } |
| 244 | |
| 245 | if (result & RELEASE_ALL_BUFFERS) { |
| 246 | for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { |
| 247 | if ((mProducerSlotSource & (1u << i)) == sourceBit) |
| 248 | mProducerBuffers[i].clear(); |
| 249 | } |
| 250 | } |
| 251 | if (result & BUFFER_NEEDS_REALLOCATION) { |
| 252 | mSource[source]->requestBuffer(*sslot, &mProducerBuffers[pslot]); |
| 253 | VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p", |
| 254 | dbgSourceStr(source), pslot, mProducerBuffers[pslot].get()); |
| 255 | } |
| 256 | |
| 257 | return result; |
| 258 | } |
| 259 | |
| 260 | status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, |
| 261 | uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { |
| 262 | VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, |
| 263 | "Unexpected dequeueBuffer() in %s state", dbgStateStr()); |
| 264 | mDbgState = DBG_STATE_GLES; |
| 265 | |
| 266 | VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#x", w, h, format, usage); |
| 267 | |
| 268 | mProducerUsage = usage | GRALLOC_USAGE_HW_COMPOSER; |
| 269 | Source source = fbSourceForCompositionType(mCompositionType); |
| 270 | if (source == SOURCE_SINK) { |
| 271 | mSinkBufferWidth = w; |
| 272 | mSinkBufferHeight = h; |
| 273 | } |
| 274 | |
| 275 | int sslot; |
| 276 | status_t result = dequeueBuffer(source, format, &sslot, fence); |
| 277 | if (result >= 0) { |
| 278 | *pslot = mapSource2ProducerSlot(source, sslot); |
| 279 | } |
| 280 | return result; |
| 281 | } |
| 282 | |
| 283 | status_t VirtualDisplaySurface::queueBuffer(int pslot, |
| 284 | const QueueBufferInput& input, QueueBufferOutput* output) { |
| 285 | VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, |
| 286 | "Unexpected queueBuffer(pslot=%d) in %s state", pslot, |
| 287 | dbgStateStr()); |
| 288 | mDbgState = DBG_STATE_GLES_DONE; |
| 289 | |
| 290 | VDS_LOGV("queueBuffer pslot=%d", pslot); |
| 291 | |
| 292 | status_t result; |
| 293 | if (mCompositionType == COMPOSITION_MIXED) { |
| 294 | // Queue the buffer back into the scratch pool |
| 295 | QueueBufferOutput scratchQBO; |
| 296 | int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot); |
| 297 | result = mBufferQueue->queueBuffer(sslot, input, &scratchQBO); |
| 298 | if (result != NO_ERROR) |
| 299 | return result; |
| 300 | |
| 301 | // Now acquire the buffer from the scratch pool -- should be the same |
| 302 | // slot and fence as we just queued. |
| 303 | Mutex::Autolock lock(mMutex); |
| 304 | BufferQueue::BufferItem item; |
| 305 | result = acquireBufferLocked(&item); |
| 306 | if (result != NO_ERROR) |
| 307 | return result; |
| 308 | VDS_LOGW_IF(item.mBuf != sslot, |
| 309 | "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d", |
| 310 | item.mBuf, sslot); |
| 311 | mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf); |
| 312 | mFbFence = mSlots[item.mBuf].mFence; |
| 313 | |
| 314 | } else { |
| 315 | LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES, |
| 316 | "Unexpected queueBuffer in state %s for compositionType %s", |
| 317 | dbgStateStr(), dbgCompositionTypeStr(mCompositionType)); |
| 318 | |
| 319 | // Extract the GLES release fence for HWC to acquire |
| 320 | int64_t timestamp; |
| 321 | Rect crop; |
| 322 | int scalingMode; |
| 323 | uint32_t transform; |
| 324 | input.deflate(×tamp, &crop, &scalingMode, &transform, |
| 325 | &mFbFence); |
| 326 | |
| 327 | mFbProducerSlot = pslot; |
| 328 | } |
| 329 | |
| 330 | *output = mQueueBufferOutput; |
| 331 | return NO_ERROR; |
| 332 | } |
| 333 | |
| 334 | void VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) { |
| 335 | VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, |
| 336 | "Unexpected cancelBuffer(pslot=%d) in %s state", pslot, |
| 337 | dbgStateStr()); |
| 338 | VDS_LOGV("cancelBuffer pslot=%d", pslot); |
| 339 | Source source = fbSourceForCompositionType(mCompositionType); |
| 340 | return mSource[source]->cancelBuffer( |
| 341 | mapProducer2SourceSlot(source, pslot), fence); |
| 342 | } |
| 343 | |
| 344 | int VirtualDisplaySurface::query(int what, int* value) { |
| 345 | return mSource[SOURCE_SINK]->query(what, value); |
| 346 | } |
| 347 | |
| 348 | status_t VirtualDisplaySurface::setSynchronousMode(bool enabled) { |
| 349 | return mSource[SOURCE_SINK]->setSynchronousMode(enabled); |
| 350 | } |
| 351 | |
| 352 | status_t VirtualDisplaySurface::connect(int api, QueueBufferOutput* output) { |
| 353 | QueueBufferOutput qbo; |
| 354 | status_t result = mSource[SOURCE_SINK]->connect(api, &qbo); |
| 355 | if (result == NO_ERROR) { |
| 356 | updateQueueBufferOutput(qbo); |
| 357 | *output = mQueueBufferOutput; |
| 358 | } |
| 359 | return result; |
| 360 | } |
| 361 | |
| 362 | status_t VirtualDisplaySurface::disconnect(int api) { |
| 363 | return mSource[SOURCE_SINK]->disconnect(api); |
| 364 | } |
| 365 | |
| 366 | void VirtualDisplaySurface::updateQueueBufferOutput( |
| 367 | const QueueBufferOutput& qbo) { |
| 368 | uint32_t w, h, transformHint, numPendingBuffers; |
| 369 | qbo.deflate(&w, &h, &transformHint, &numPendingBuffers); |
| 370 | mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers); |
| 371 | } |
| 372 | |
| 373 | void VirtualDisplaySurface::resetPerFrameState() { |
| 374 | mCompositionType = COMPOSITION_UNKNOWN; |
| 375 | mSinkBufferWidth = 0; |
| 376 | mSinkBufferHeight = 0; |
| 377 | mFbFence = Fence::NO_FENCE; |
| 378 | mFbProducerSlot = -1; |
| 379 | mOutputProducerSlot = -1; |
| 380 | } |
| 381 | |
| 382 | // This slot mapping function is its own inverse, so two copies are unnecessary. |
| 383 | // Both are kept to make the intent clear where the function is called, and for |
| 384 | // the (unlikely) chance that we switch to a different mapping function. |
| 385 | int VirtualDisplaySurface::mapSource2ProducerSlot(Source source, int sslot) { |
| 386 | if (source == SOURCE_SCRATCH) { |
| 387 | return BufferQueue::NUM_BUFFER_SLOTS - sslot - 1; |
| 388 | } else { |
| 389 | return sslot; |
| 390 | } |
| 391 | } |
| 392 | int VirtualDisplaySurface::mapProducer2SourceSlot(Source source, int pslot) { |
| 393 | return mapSource2ProducerSlot(source, pslot); |
| 394 | } |
| 395 | |
| 396 | VirtualDisplaySurface::Source |
| 397 | VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) { |
| 398 | return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK; |
| 399 | } |
| 400 | |
| 401 | const char* VirtualDisplaySurface::dbgStateStr() const { |
| 402 | switch (mDbgState) { |
| 403 | case DBG_STATE_IDLE: return "IDLE"; |
| 404 | case DBG_STATE_PREPARED: return "PREPARED"; |
| 405 | case DBG_STATE_GLES: return "GLES"; |
| 406 | case DBG_STATE_GLES_DONE: return "GLES_DONE"; |
| 407 | case DBG_STATE_HWC: return "HWC"; |
| 408 | default: return "INVALID"; |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | const char* VirtualDisplaySurface::dbgSourceStr(Source s) { |
| 413 | switch (s) { |
| 414 | case SOURCE_SINK: return "SINK"; |
| 415 | case SOURCE_SCRATCH: return "SCRATCH"; |
| 416 | default: return "INVALID"; |
| 417 | } |
| 418 | } |
| 419 | |
Jesse Hall | 99c7dbb | 2013-03-14 14:29:29 -0700 | [diff] [blame] | 420 | // --------------------------------------------------------------------------- |
| 421 | } // namespace android |
| 422 | // --------------------------------------------------------------------------- |