Loading libs/gui/Surface.cpp +335 −52 Original line number Diff line number Diff line Loading @@ -618,29 +618,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) { Loading @@ -658,16 +660,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; } Loading Loading @@ -696,7 +699,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { freeAllBuffers(); } if (enableFrameTimestamps) { if (dqInput.getTimestamps) { mFrameEventHistory->applyDelta(frameTimestamps); } Loading Loading @@ -739,6 +742,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(); Loading Loading @@ -769,15 +942,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; } Loading @@ -787,42 +1010,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, Loading Loading @@ -893,15 +1096,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { input.setSurfaceDamage(flippedRegion); } 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); *out = input; } mDequeuedSlots.erase(i); void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, const IGraphicBufferProducer::QueueBufferOutput& output) { mDequeuedSlots.erase(slot); if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); Loading Loading @@ -935,7 +1135,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; } Loading @@ -945,6 +1145,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; } Loading libs/gui/include/gui/Surface.h +25 −0 Original line number Diff line number Diff line Loading @@ -344,6 +344,23 @@ public: static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, ui::Dataspace dataspace); // Batch version of dequeueBuffer, cancelBuffer and queueBuffer // Note that these batched operations are not supported when shared buffer mode is being used. struct BatchBuffer { ANativeWindowBuffer* buffer = nullptr; int fenceFd = -1; }; virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers); virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers); struct BatchQueuedBuffer { ANativeWindowBuffer* buffer = nullptr; int fenceFd = -1; nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; }; virtual int queueBuffers( const std::vector<BatchQueuedBuffer>& buffers); protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; Loading Loading @@ -373,6 +390,14 @@ protected: void freeAllBuffers(); int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput); void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out); void onBufferQueuedLocked(int slot, sp<Fence> fence, const IGraphicBufferProducer::QueueBufferOutput& output); struct BufferSlot { sp<GraphicBuffer> buffer; Region dirtyRegion; Loading libs/gui/tests/Surface_test.cpp +82 −0 Original line number Diff line number Diff line Loading @@ -2024,4 +2024,86 @@ TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count); } TEST_F(SurfaceTest, BatchOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); sp<StubProducerListener> listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); // Batch dequeued buffers can be queued individually ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); for (size_t i = 0; i < BATCH_SIZE; i++) { ANativeWindowBuffer* buffer = buffers[i].buffer; int fence = buffers[i].fenceFd; ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } // Batch dequeued buffers can be canceled individually ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); for (size_t i = 0; i < BATCH_SIZE; i++) { ANativeWindowBuffer* buffer = buffers[i].buffer; int fence = buffers[i].fenceFd; ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); } // Batch dequeued buffers can be batch cancelled ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers)); // Batch dequeued buffers can be batch queued ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); for (size_t i = 0; i < BATCH_SIZE; i++) { queuedBuffers[i].buffer = buffers[i].buffer; queuedBuffers[i].fenceFd = buffers[i].fenceFd; queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; } ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers)); ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } TEST_F(SurfaceTest, BatchIllegalOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); sp<StubProducerListener> listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); // Batch operations are invalid in shared buffer mode surface->setSharedBufferMode(true); ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers)); ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers)); ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers)); surface->setSharedBufferMode(false); ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } } // namespace android Loading
libs/gui/Surface.cpp +335 −52 Original line number Diff line number Diff line Loading @@ -618,29 +618,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) { Loading @@ -658,16 +660,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; } Loading Loading @@ -696,7 +699,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { freeAllBuffers(); } if (enableFrameTimestamps) { if (dqInput.getTimestamps) { mFrameEventHistory->applyDelta(frameTimestamps); } Loading Loading @@ -739,6 +742,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(); Loading Loading @@ -769,15 +942,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; } Loading @@ -787,42 +1010,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, Loading Loading @@ -893,15 +1096,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { input.setSurfaceDamage(flippedRegion); } 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); *out = input; } mDequeuedSlots.erase(i); void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, const IGraphicBufferProducer::QueueBufferOutput& output) { mDequeuedSlots.erase(slot); if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); Loading Loading @@ -935,7 +1135,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; } Loading @@ -945,6 +1145,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; } Loading
libs/gui/include/gui/Surface.h +25 −0 Original line number Diff line number Diff line Loading @@ -344,6 +344,23 @@ public: static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, ui::Dataspace dataspace); // Batch version of dequeueBuffer, cancelBuffer and queueBuffer // Note that these batched operations are not supported when shared buffer mode is being used. struct BatchBuffer { ANativeWindowBuffer* buffer = nullptr; int fenceFd = -1; }; virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers); virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers); struct BatchQueuedBuffer { ANativeWindowBuffer* buffer = nullptr; int fenceFd = -1; nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; }; virtual int queueBuffers( const std::vector<BatchQueuedBuffer>& buffers); protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; Loading Loading @@ -373,6 +390,14 @@ protected: void freeAllBuffers(); int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput); void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out); void onBufferQueuedLocked(int slot, sp<Fence> fence, const IGraphicBufferProducer::QueueBufferOutput& output); struct BufferSlot { sp<GraphicBuffer> buffer; Region dirtyRegion; Loading
libs/gui/tests/Surface_test.cpp +82 −0 Original line number Diff line number Diff line Loading @@ -2024,4 +2024,86 @@ TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count); } TEST_F(SurfaceTest, BatchOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); sp<StubProducerListener> listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); // Batch dequeued buffers can be queued individually ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); for (size_t i = 0; i < BATCH_SIZE; i++) { ANativeWindowBuffer* buffer = buffers[i].buffer; int fence = buffers[i].fenceFd; ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } // Batch dequeued buffers can be canceled individually ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); for (size_t i = 0; i < BATCH_SIZE; i++) { ANativeWindowBuffer* buffer = buffers[i].buffer; int fence = buffers[i].fenceFd; ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); } // Batch dequeued buffers can be batch cancelled ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers)); // Batch dequeued buffers can be batch queued ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); for (size_t i = 0; i < BATCH_SIZE; i++) { queuedBuffers[i].buffer = buffers[i].buffer; queuedBuffers[i].fenceFd = buffers[i].fenceFd; queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; } ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers)); ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } TEST_F(SurfaceTest, BatchIllegalOperations) { const int BUFFER_COUNT = 16; const int BATCH_SIZE = 8; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); sp<StubProducerListener> listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/false)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); // Batch operations are invalid in shared buffer mode surface->setSharedBufferMode(true); ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers)); ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers)); ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers)); surface->setSharedBufferMode(false); ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } } // namespace android