diff options
Diffstat (limited to 'libs/gui/Surface.cpp')
| -rw-r--r-- | libs/gui/Surface.cpp | 451 |
1 files changed, 380 insertions, 71 deletions
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 8a64a4f0f7..2edb4e4ba4 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -34,9 +34,9 @@ #include <utils/NativeHandle.h> #include <ui/DisplayStatInfo.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> -#include <ui/HdrCapabilities.h> #include <ui/Region.h> #include <gui/BufferItem.h> @@ -48,7 +48,6 @@ namespace android { -using ui::ColorMode; using ui::Dataspace; namespace { @@ -63,13 +62,15 @@ bool isInterceptorRegistrationOp(int op) { } // namespace -Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp) +Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp, + const sp<IBinder>& surfaceControlHandle) : mGraphicBufferProducer(bufferProducer), mCrop(Rect::EMPTY_RECT), mBufferAge(0), mGenerationNumber(0), mSharedBufferMode(false), mAutoRefresh(false), + mAutoPrerotation(false), mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), mSharedBufferHasBeenQueued(false), mQueriedSupportedTimestamps(false), @@ -111,6 +112,7 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll mProducerControlledByApp = controlledByApp; mSwapIntervalZero = false; mMaxBufferCount = NUM_BUFFER_SLOTS; + mSurfaceControlHandle = surfaceControlHandle; } Surface::~Surface() { @@ -359,15 +361,12 @@ status_t Surface::getHdrSupport(bool* supported) { return NAME_NOT_FOUND; } - HdrCapabilities hdrCapabilities; - status_t err = - composerService()->getHdrCapabilities(display, &hdrCapabilities); - - if (err) + ui::DynamicDisplayInfo info; + if (status_t err = composerService()->getDynamicDisplayInfo(display, &info); err != NO_ERROR) { return err; + } - *supported = !hdrCapabilities.getSupportedHdrTypes().empty(); - + *supported = !info.hdrCapabilities.getSupportedHdrTypes().empty(); return NO_ERROR; } @@ -616,29 +615,31 @@ private: std::mutex mMutex; }; +void Surface::getDequeueBufferInputLocked( + IGraphicBufferProducer::DequeueBufferInput* dequeueInput) { + LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null"); + + dequeueInput->width = mReqWidth ? mReqWidth : mUserWidth; + dequeueInput->height = mReqHeight ? mReqHeight : mUserHeight; + + dequeueInput->format = mReqFormat; + dequeueInput->usage = mReqUsage; + + dequeueInput->getTimestamps = mEnableFrameTimestamps; +} + int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); - uint32_t reqWidth; - uint32_t reqHeight; - PixelFormat reqFormat; - uint64_t reqUsage; - bool enableFrameTimestamps; - + IGraphicBufferProducer::DequeueBufferInput dqInput; { Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } - reqWidth = mReqWidth ? mReqWidth : mUserWidth; - reqHeight = mReqHeight ? mReqHeight : mUserHeight; - - reqFormat = mReqFormat; - reqUsage = mReqUsage; - - enableFrameTimestamps = mEnableFrameTimestamps; + getDequeueBufferInputLocked(&dqInput); if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { @@ -656,16 +657,17 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { nsecs_t startTime = systemTime(); FrameEventHistoryDelta frameTimestamps; - status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, - reqFormat, reqUsage, &mBufferAge, - enableFrameTimestamps ? &frameTimestamps - : nullptr); + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width, + dqInput.height, dqInput.format, + dqInput.usage, &mBufferAge, + dqInput.getTimestamps ? + &frameTimestamps : nullptr); mLastDequeueDuration = systemTime() - startTime; if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer" "(%d, %d, %d, %#" PRIx64 ") failed: %d", - reqWidth, reqHeight, reqFormat, reqUsage, result); + dqInput.width, dqInput.height, dqInput.format, dqInput.usage, result); return result; } @@ -694,7 +696,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { freeAllBuffers(); } - if (enableFrameTimestamps) { + if (dqInput.getTimestamps) { mFrameEventHistory->applyDelta(frameTimestamps); } @@ -737,6 +739,176 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return OK; } +int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) { + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + + ATRACE_CALL(); + ALOGV("Surface::dequeueBuffers"); + + if (buffers->size() == 0) { + ALOGE("%s: must dequeue at least 1 buffer!", __FUNCTION__); + return BAD_VALUE; + } + + if (mSharedBufferMode) { + ALOGE("%s: batch operation is not supported in shared buffer mode!", + __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBufferRequested = buffers->size(); + DequeueBufferInput input; + + { + Mutex::Autolock lock(mMutex); + if (mReportRemovedBuffers) { + mRemovedBuffers.clear(); + } + + getDequeueBufferInputLocked(&input); + } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffers + + std::vector<DequeueBufferInput> dequeueInput(numBufferRequested, input); + std::vector<DequeueBufferOutput> dequeueOutput; + + nsecs_t startTime = systemTime(); + + status_t result = mGraphicBufferProducer->dequeueBuffers(dequeueInput, &dequeueOutput); + + mLastDequeueDuration = systemTime() - startTime; + + if (result < 0) { + ALOGV("%s: IGraphicBufferProducer::dequeueBuffers" + "(%d, %d, %d, %#" PRIx64 ") failed: %d", + __FUNCTION__, input.width, input.height, input.format, input.usage, result); + return result; + } + + std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested); + std::vector<status_t> cancelBufferOutputs; + for (size_t i = 0; i < numBufferRequested; i++) { + cancelBufferInputs[i].slot = dequeueOutput[i].slot; + cancelBufferInputs[i].fence = dequeueOutput[i].fence; + } + + for (const auto& output : dequeueOutput) { + if (output.result < 0) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGV("%s: IGraphicBufferProducer::dequeueBuffers" + "(%d, %d, %d, %#" PRIx64 ") failed: %d", + __FUNCTION__, input.width, input.height, input.format, input.usage, + output.result); + return output.result; + } + + if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d", + __FUNCTION__, output.slot); + android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging + return FAILED_TRANSACTION; + } + + if (input.getTimestamps && !output.timestamps.has_value()) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGE("%s: no frame timestamp returns!", __FUNCTION__); + return FAILED_TRANSACTION; + } + + // this should never happen + ALOGE_IF(output.fence == nullptr, + "%s: received null Fence! slot=%d", __FUNCTION__, output.slot); + } + + Mutex::Autolock lock(mMutex); + + // Write this while holding the mutex + mLastDequeueStartTime = startTime; + + std::vector<int32_t> requestBufferSlots; + requestBufferSlots.reserve(numBufferRequested); + // handle release all buffers and request buffers + for (const auto& output : dequeueOutput) { + if (output.result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { + ALOGV("%s: RELEASE_ALL_BUFFERS during batch operation", __FUNCTION__); + freeAllBuffers(); + break; + } + } + + for (const auto& output : dequeueOutput) { + // Collect slots that needs requesting buffer + sp<GraphicBuffer>& gbuf(mSlots[output.slot].buffer); + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) { + if (mReportRemovedBuffers && (gbuf != nullptr)) { + mRemovedBuffers.push_back(gbuf); + } + requestBufferSlots.push_back(output.slot); + } + } + + // Batch request Buffer + std::vector<RequestBufferOutput> reqBufferOutput; + if (requestBufferSlots.size() > 0) { + result = mGraphicBufferProducer->requestBuffers(requestBufferSlots, &reqBufferOutput); + if (result != NO_ERROR) { + ALOGE("%s: IGraphicBufferProducer::requestBuffers failed: %d", + __FUNCTION__, result); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + return result; + } + + // Check if we have any single failure + for (size_t i = 0; i < requestBufferSlots.size(); i++) { + if (reqBufferOutput[i].result != OK) { + ALOGE("%s: IGraphicBufferProducer::requestBuffers failed at %zu-th buffer, slot %d", + __FUNCTION__, i, requestBufferSlots[i]); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + return reqBufferOutput[i].result; + } + } + + // Fill request buffer results to mSlots + for (size_t i = 0; i < requestBufferSlots.size(); i++) { + mSlots[requestBufferSlots[i]].buffer = reqBufferOutput[i].buffer; + } + } + + for (size_t batchIdx = 0; batchIdx < numBufferRequested; batchIdx++) { + const auto& output = dequeueOutput[batchIdx]; + int slot = output.slot; + sp<GraphicBuffer>& gbuf(mSlots[slot].buffer); + + if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { + static FenceMonitor hwcReleaseThread("HWC release"); + hwcReleaseThread.queueFence(output.fence); + } + + if (input.getTimestamps) { + mFrameEventHistory->applyDelta(output.timestamps.value()); + } + + if (output.fence->isValid()) { + buffers->at(batchIdx).fenceFd = output.fence->dup(); + if (buffers->at(batchIdx).fenceFd == -1) { + ALOGE("%s: error duping fence: %d", __FUNCTION__, errno); + // dup() should never fail; something is badly wrong. Soldier on + // and hope for the best; the worst that should happen is some + // visible corruption that lasts until the next frame. + } + } else { + buffers->at(batchIdx).fenceFd = -1; + } + + buffers->at(batchIdx).buffer = gbuf.get(); + mDequeuedSlots.insert(slot); + } + return OK; +} + int Surface::cancelBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); @@ -767,15 +939,65 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, return OK; } +int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) { + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + ATRACE_CALL(); + ALOGV("Surface::cancelBuffers"); + + if (mSharedBufferMode) { + ALOGE("%s: batch operation is not supported in shared buffer mode!", + __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBuffers = buffers.size(); + std::vector<CancelBufferInput> cancelBufferInputs(numBuffers); + std::vector<status_t> cancelBufferOutputs; + size_t numBuffersCancelled = 0; + int badSlotResult = 0; + for (size_t i = 0; i < numBuffers; i++) { + int slot = getSlotFromBufferLocked(buffers[i].buffer); + int fenceFd = buffers[i].fenceFd; + if (slot < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__); + badSlotResult = slot; + } else { + sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + cancelBufferInputs[numBuffersCancelled].slot = slot; + cancelBufferInputs[numBuffersCancelled++].fence = fence; + } + } + cancelBufferInputs.resize(numBuffersCancelled); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + + + for (size_t i = 0; i < numBuffersCancelled; i++) { + mDequeuedSlots.erase(cancelBufferInputs[i].slot); + } + + if (badSlotResult != 0) { + return badSlotResult; + } + return OK; +} + int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { + if (buffer == nullptr) { + ALOGE("%s: input buffer is null!", __FUNCTION__); + return BAD_VALUE; + } + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } } - ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + ALOGE("%s: unknown buffer: %p", __FUNCTION__, buffer->handle); return BAD_VALUE; } @@ -785,42 +1007,22 @@ int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__ return OK; } -int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { - ATRACE_CALL(); - ALOGV("Surface::queueBuffer"); - Mutex::Autolock lock(mMutex); - int64_t timestamp; +void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, + nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) { bool isAutoTimestamp = false; - if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { + if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { timestamp = systemTime(SYSTEM_TIME_MONOTONIC); isAutoTimestamp = true; ALOGV("Surface::queueBuffer making up timestamp: %.2f ms", timestamp / 1000000.0); - } else { - timestamp = mTimestamp; - } - int i = getSlotFromBufferLocked(buffer); - if (i < 0) { - if (fenceFd >= 0) { - close(fenceFd); - } - return i; - } - if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { - if (fenceFd >= 0) { - close(fenceFd); - } - return OK; } - // Make sure the crop rectangle is entirely inside the buffer. Rect crop(Rect::EMPTY_RECT); mCrop.intersect(Rect(buffer->width, buffer->height), &crop); sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); - IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast<android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, @@ -891,15 +1093,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { input.setSurfaceDamage(flippedRegion); } + *out = input; +} - nsecs_t now = systemTime(); - status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); - mLastQueueDuration = systemTime() - now; - if (err != OK) { - ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); - } - - mDequeuedSlots.erase(i); +void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, + const IGraphicBufferProducer::QueueBufferOutput& output) { + mDequeuedSlots.erase(slot); if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); @@ -933,7 +1132,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { mDirtyRegion = Region::INVALID_REGION; } - if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { + if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == slot) { mSharedBufferHasBeenQueued = true; } @@ -943,6 +1142,89 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { static FenceMonitor gpuCompletionThread("GPU completion"); gpuCompletionThread.queueFence(fence); } +} + +int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffer"); + Mutex::Autolock lock(mMutex); + + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + return i; + } + if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { + if (fenceFd >= 0) { + close(fenceFd); + } + return OK; + } + + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input); + sp<Fence> fence = input.fence; + + nsecs_t now = systemTime(); + status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer, %d", err); + } + + onBufferQueuedLocked(i, fence, output); + return err; +} + +int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffers"); + Mutex::Autolock lock(mMutex); + + if (mSharedBufferMode) { + ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBuffers = buffers.size(); + std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers); + std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs; + std::vector<int> bufferSlots(numBuffers, -1); + std::vector<sp<Fence>> bufferFences(numBuffers); + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + int i = getSlotFromBufferLocked(buffers[batchIdx].buffer); + if (i < 0) { + if (buffers[batchIdx].fenceFd >= 0) { + close(buffers[batchIdx].fenceFd); + } + return i; + } + bufferSlots[batchIdx] = i; + + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked( + buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp, + &input); + bufferFences[batchIdx] = input.fence; + queueBufferInputs[batchIdx] = input; + } + + nsecs_t now = systemTime(); + status_t err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err); + } + + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx], + queueBufferOutputs[batchIdx]); + } return err; } @@ -983,8 +1265,15 @@ int Surface::query(int what, int* value) const { } break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { - if (composerService()->authenticateSurfaceTexture( - mGraphicBufferProducer)) { + status_t err = mGraphicBufferProducer->query(what, value); + if (err == NO_ERROR) { + return NO_ERROR; + } + sp<ISurfaceComposer> surfaceComposer = composerService(); + if (surfaceComposer == nullptr) { + return -EPERM; // likely permissions error + } + if (surfaceComposer->authenticateSurfaceTexture(mGraphicBufferProducer)) { *value = 1; } else { *value = 0; @@ -1003,7 +1292,7 @@ int Surface::query(int what, int* value) const { mUserHeight ? mUserHeight : mDefaultHeight); return NO_ERROR; case NATIVE_WINDOW_TRANSFORM_HINT: - *value = static_cast<int>(mTransformHint); + *value = static_cast<int>(getTransformHint()); return NO_ERROR; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: { status_t err = NO_ERROR; @@ -1210,6 +1499,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2: res = dispatchGetLastQueuedBuffer2(args); break; + case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO: + res = dispatchSetFrameTimelineInfo(args); + break; default: res = NAME_NOT_FOUND; break; @@ -1441,7 +1733,8 @@ int Surface::dispatchGetLastQueueDuration(va_list args) { int Surface::dispatchSetFrameRate(va_list args) { float frameRate = static_cast<float>(va_arg(args, double)); int8_t compatibility = static_cast<int8_t>(va_arg(args, int)); - return setFrameRate(frameRate, compatibility); + int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int)); + return setFrameRate(frameRate, compatibility, changeFrameRateStrategy); } int Surface::dispatchAddCancelInterceptor(va_list args) { @@ -1502,7 +1795,7 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix); if (graphicBuffer != nullptr) { - *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()); + *buffer = graphicBuffer->toAHardwareBuffer(); AHardwareBuffer_acquire(*buffer); } else { *buffer = nullptr; @@ -1549,7 +1842,16 @@ int Surface::dispatchGetLastQueuedBuffer2(va_list args) { return result; } -bool Surface::transformToDisplayInverse() { +int Surface::dispatchSetFrameTimelineInfo(va_list args) { + ATRACE_CALL(); + auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t)); + auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t)); + + ALOGV("Surface::%s", __func__); + return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId}); +} + +bool Surface::transformToDisplayInverse() const { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; } @@ -2302,15 +2604,22 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ mSurfaceListener->onBuffersDiscarded(discardedBufs); } -status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { +status_t Surface::setFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) { ATRACE_CALL(); ALOGV("Surface::setFrameRate"); - if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) { + if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy, + "Surface::setFrameRate")) { return BAD_VALUE; } - return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility); + return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility, + changeFrameRateStrategy); +} + +status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) { + return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo); } }; // namespace android |