/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AndroidHardwareBuffer.h" #include #include "mozilla/gfx/2D.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/TextureClientSharedSurface.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtrExtensions.h" namespace mozilla { namespace layers { static uint32_t ToAHardwareBuffer_Format(gfx::SurfaceFormat aFormat) { switch (aFormat) { case gfx::SurfaceFormat::R8G8B8A8: case gfx::SurfaceFormat::B8G8R8A8: return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; case gfx::SurfaceFormat::R8G8B8X8: case gfx::SurfaceFormat::B8G8R8X8: return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM; case gfx::SurfaceFormat::R5G6B5_UINT16: return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; default: MOZ_ASSERT_UNREACHABLE("Unsupported SurfaceFormat"); return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; } } StaticAutoPtr AndroidHardwareBufferApi::sInstance; /* static */ void AndroidHardwareBufferApi::Init() { sInstance = new AndroidHardwareBufferApi(); if (!sInstance->Load()) { sInstance = nullptr; } } /* static */ void AndroidHardwareBufferApi::Shutdown() { sInstance = nullptr; } AndroidHardwareBufferApi::AndroidHardwareBufferApi() {} bool AndroidHardwareBufferApi::Load() { void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); MOZ_ASSERT(handle); if (!handle) { gfxCriticalNote << "Failed to load libandroid.so"; return false; } mAHardwareBuffer_allocate = (_AHardwareBuffer_allocate)dlsym(handle, "AHardwareBuffer_allocate"); mAHardwareBuffer_acquire = (_AHardwareBuffer_acquire)dlsym(handle, "AHardwareBuffer_acquire"); mAHardwareBuffer_release = (_AHardwareBuffer_release)dlsym(handle, "AHardwareBuffer_release"); mAHardwareBuffer_describe = (_AHardwareBuffer_describe)dlsym(handle, "AHardwareBuffer_describe"); mAHardwareBuffer_lock = (_AHardwareBuffer_lock)dlsym(handle, "AHardwareBuffer_lock"); mAHardwareBuffer_unlock = (_AHardwareBuffer_unlock)dlsym(handle, "AHardwareBuffer_unlock"); mAHardwareBuffer_sendHandleToUnixSocket = (_AHardwareBuffer_sendHandleToUnixSocket)dlsym( handle, "AHardwareBuffer_sendHandleToUnixSocket"); mAHardwareBuffer_recvHandleFromUnixSocket = (_AHardwareBuffer_recvHandleFromUnixSocket)dlsym( handle, "AHardwareBuffer_recvHandleFromUnixSocket"); if (!mAHardwareBuffer_allocate || !mAHardwareBuffer_acquire || !mAHardwareBuffer_release || !mAHardwareBuffer_describe || !mAHardwareBuffer_lock || !mAHardwareBuffer_unlock || !mAHardwareBuffer_sendHandleToUnixSocket || !mAHardwareBuffer_recvHandleFromUnixSocket) { gfxCriticalNote << "Failed to load AHardwareBuffer"; return false; } return true; } void AndroidHardwareBufferApi::Allocate(const AHardwareBuffer_Desc* aDesc, AHardwareBuffer** aOutBuffer) { mAHardwareBuffer_allocate(aDesc, aOutBuffer); } void AndroidHardwareBufferApi::Acquire(AHardwareBuffer* aBuffer) { mAHardwareBuffer_acquire(aBuffer); } void AndroidHardwareBufferApi::Release(AHardwareBuffer* aBuffer) { mAHardwareBuffer_release(aBuffer); } void AndroidHardwareBufferApi::Describe(const AHardwareBuffer* aBuffer, AHardwareBuffer_Desc* aOutDesc) { mAHardwareBuffer_describe(aBuffer, aOutDesc); } int AndroidHardwareBufferApi::Lock(AHardwareBuffer* aBuffer, uint64_t aUsage, int32_t aFence, const ARect* aRect, void** aOutVirtualAddress) { return mAHardwareBuffer_lock(aBuffer, aUsage, aFence, aRect, aOutVirtualAddress); } int AndroidHardwareBufferApi::Unlock(AHardwareBuffer* aBuffer, int32_t* aFence) { return mAHardwareBuffer_unlock(aBuffer, aFence); } int AndroidHardwareBufferApi::SendHandleToUnixSocket( const AHardwareBuffer* aBuffer, int aSocketFd) { return mAHardwareBuffer_sendHandleToUnixSocket(aBuffer, aSocketFd); } int AndroidHardwareBufferApi::RecvHandleFromUnixSocket( int aSocketFd, AHardwareBuffer** aOutBuffer) { return mAHardwareBuffer_recvHandleFromUnixSocket(aSocketFd, aOutBuffer); } /* static */ uint64_t AndroidHardwareBuffer::GetNextId() { static std::atomic sNextId = 0; uint64_t id = ++sNextId; return id; } /* static */ already_AddRefed AndroidHardwareBuffer::Create( gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { if (!AndroidHardwareBufferApi::Get()) { return nullptr; } if (aFormat != gfx::SurfaceFormat::R8G8B8A8 && aFormat != gfx::SurfaceFormat::R8G8B8X8 && aFormat != gfx::SurfaceFormat::B8G8R8A8 && aFormat != gfx::SurfaceFormat::B8G8R8X8 && aFormat != gfx::SurfaceFormat::R5G6B5_UINT16) { return nullptr; } AHardwareBuffer_Desc desc = {}; desc.width = aSize.width; desc.height = aSize.height; desc.layers = 1; // number of images desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT; desc.format = ToAHardwareBuffer_Format(aFormat); AHardwareBuffer* nativeBuffer = nullptr; AndroidHardwareBufferApi::Get()->Allocate(&desc, &nativeBuffer); if (!nativeBuffer) { return nullptr; } AHardwareBuffer_Desc bufferInfo = {}; AndroidHardwareBufferApi::Get()->Describe(nativeBuffer, &bufferInfo); RefPtr buffer = new AndroidHardwareBuffer( nativeBuffer, aSize, bufferInfo.stride, aFormat, GetNextId()); AndroidHardwareBufferManager::Get()->Register(buffer); return buffer.forget(); } /* static */ already_AddRefed AndroidHardwareBuffer::FromFileDescriptor(ipc::FileDescriptor& aFileDescriptor, uint64_t aBufferId, gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { if (!aFileDescriptor.IsValid()) { gfxCriticalNote << "AndroidHardwareBuffer invalid FileDescriptor"; return nullptr; } ipc::FileDescriptor& fileDescriptor = const_cast(aFileDescriptor); auto rawFD = fileDescriptor.TakePlatformHandle(); AHardwareBuffer* nativeBuffer = nullptr; int ret = AndroidHardwareBufferApi::Get()->RecvHandleFromUnixSocket( rawFD.get(), &nativeBuffer); if (ret < 0) { gfxCriticalNote << "RecvHandleFromUnixSocket failed"; return nullptr; } AHardwareBuffer_Desc bufferInfo = {}; AndroidHardwareBufferApi::Get()->Describe(nativeBuffer, &bufferInfo); RefPtr buffer = new AndroidHardwareBuffer( nativeBuffer, aSize, bufferInfo.stride, aFormat, aBufferId); return buffer.forget(); } AndroidHardwareBuffer::AndroidHardwareBuffer(AHardwareBuffer* aNativeBuffer, gfx::IntSize aSize, uint32_t aStride, gfx::SurfaceFormat aFormat, uint64_t aId) : mSize(aSize), mStride(aStride), mFormat(aFormat), mId(aId), mNativeBuffer(aNativeBuffer), mIsRegistered(false) { MOZ_ASSERT(mNativeBuffer); #ifdef DEBUG AHardwareBuffer_Desc bufferInfo = {}; AndroidHardwareBufferApi::Get()->Describe(mNativeBuffer, &bufferInfo); MOZ_ASSERT(mSize.width == (int32_t)bufferInfo.width); MOZ_ASSERT(mSize.height == (int32_t)bufferInfo.height); MOZ_ASSERT(mStride == bufferInfo.stride); MOZ_ASSERT(ToAHardwareBuffer_Format(mFormat) == bufferInfo.format); #endif } AndroidHardwareBuffer::~AndroidHardwareBuffer() { if (mIsRegistered) { AndroidHardwareBufferManager::Get()->Unregister(this); } AndroidHardwareBufferApi::Get()->Release(mNativeBuffer); } int AndroidHardwareBuffer::Lock(uint64_t aUsage, const ARect* aRect, void** aOutVirtualAddress) { ipc::FileDescriptor fd = GetAndResetReleaseFence(); int32_t fenceFd = -1; ipc::FileDescriptor::UniquePlatformHandle rawFd; if (fd.IsValid()) { rawFd = fd.TakePlatformHandle(); fenceFd = rawFd.get(); } return AndroidHardwareBufferApi::Get()->Lock(mNativeBuffer, aUsage, fenceFd, aRect, aOutVirtualAddress); } int AndroidHardwareBuffer::Unlock() { int rawFd = -1; // XXX All tested recent Android devices did not return valid fence. int ret = AndroidHardwareBufferApi::Get()->Unlock(mNativeBuffer, &rawFd); if (ret != 0) { return ret; } ipc::FileDescriptor acquireFenceFd; // The value -1 indicates that unlocking has already completed before // the function returned and no further operations are necessary. if (rawFd >= 0) { acquireFenceFd = ipc::FileDescriptor(UniqueFileHandle(rawFd)); } if (acquireFenceFd.IsValid()) { SetAcquireFence(std::move(acquireFenceFd)); } return 0; } int AndroidHardwareBuffer::SendHandleToUnixSocket(int aSocketFd) { return AndroidHardwareBufferApi::Get()->SendHandleToUnixSocket(mNativeBuffer, aSocketFd); } void AndroidHardwareBuffer::SetLastFwdTransactionId( uint64_t aFwdTransactionId, bool aUsesImageBridge, const MonitorAutoLock& aAutoLock) { if (mTransactionId.isNothing()) { mTransactionId = Some(FwdTransactionId(aFwdTransactionId, aUsesImageBridge)); return; } MOZ_RELEASE_ASSERT(mTransactionId.ref().mUsesImageBridge == aUsesImageBridge); MOZ_RELEASE_ASSERT(mTransactionId.ref().mId <= aFwdTransactionId); mTransactionId.ref().mId = aFwdTransactionId; } uint64_t AndroidHardwareBuffer::GetLastFwdTransactionId( const MonitorAutoLock& aAutoLock) { if (mTransactionId.isNothing()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return 0; } return mTransactionId.ref().mId; } void AndroidHardwareBuffer::SetReleaseFence(ipc::FileDescriptor&& aFenceFd) { MonitorAutoLock lock(AndroidHardwareBufferManager::Get()->GetMonitor()); SetReleaseFence(std::move(aFenceFd), lock); } void AndroidHardwareBuffer::SetReleaseFence(ipc::FileDescriptor&& aFenceFd, const MonitorAutoLock& aAutoLock) { mReleaseFenceFd = std::move(aFenceFd); } void AndroidHardwareBuffer::SetAcquireFence(ipc::FileDescriptor&& aFenceFd) { MonitorAutoLock lock(AndroidHardwareBufferManager::Get()->GetMonitor()); mAcquireFenceFd = std::move(aFenceFd); } ipc::FileDescriptor AndroidHardwareBuffer::GetAndResetReleaseFence() { MonitorAutoLock lock(AndroidHardwareBufferManager::Get()->GetMonitor()); if (!mReleaseFenceFd.IsValid()) { return ipc::FileDescriptor(); } return std::move(mReleaseFenceFd); } ipc::FileDescriptor AndroidHardwareBuffer::GetAndResetAcquireFence() { MonitorAutoLock lock(AndroidHardwareBufferManager::Get()->GetMonitor()); if (!mAcquireFenceFd.IsValid()) { return ipc::FileDescriptor(); } return std::move(mAcquireFenceFd); } ipc::FileDescriptor AndroidHardwareBuffer::GetAcquireFence() { MonitorAutoLock lock(AndroidHardwareBufferManager::Get()->GetMonitor()); if (!mAcquireFenceFd.IsValid()) { return ipc::FileDescriptor(); } return mAcquireFenceFd; } bool AndroidHardwareBuffer::WaitForBufferOwnership() { return AndroidHardwareBufferManager::Get()->WaitForBufferOwnership(this); } bool AndroidHardwareBuffer::IsWaitingForBufferOwnership() { return AndroidHardwareBufferManager::Get()->IsWaitingForBufferOwnership(this); } RefPtr AndroidHardwareBuffer::GetTextureClientOfSharedSurfaceTextureData( const layers::SurfaceDescriptor& aDesc, const gfx::SurfaceFormat aFormat, const gfx::IntSize& aSize, const TextureFlags aFlags, LayersIPCChannel* aAllocator) { if (mTextureClientOfSharedSurfaceTextureData) { return mTextureClientOfSharedSurfaceTextureData; } mTextureClientOfSharedSurfaceTextureData = SharedSurfaceTextureData::CreateTextureClient(aDesc, aFormat, aSize, aFlags, aAllocator); return mTextureClientOfSharedSurfaceTextureData; } StaticAutoPtr AndroidHardwareBufferManager::sInstance; /* static */ void AndroidHardwareBufferManager::Init() { sInstance = new AndroidHardwareBufferManager(); } /* static */ void AndroidHardwareBufferManager::Shutdown() { sInstance = nullptr; } AndroidHardwareBufferManager::AndroidHardwareBufferManager() : mMonitor("AndroidHardwareBufferManager.mMonitor") {} void AndroidHardwareBufferManager::Register( RefPtr aBuffer) { MonitorAutoLock lock(mMonitor); aBuffer->mIsRegistered = true; ThreadSafeWeakPtr weak(aBuffer); #ifdef DEBUG const auto it = mBuffers.find(aBuffer->mId); MOZ_ASSERT(it == mBuffers.end()); #endif mBuffers.emplace(aBuffer->mId, weak); } void AndroidHardwareBufferManager::Unregister(AndroidHardwareBuffer* aBuffer) { MonitorAutoLock lock(mMonitor); const auto it = mBuffers.find(aBuffer->mId); MOZ_ASSERT(it != mBuffers.end()); if (it == mBuffers.end()) { gfxCriticalNote << "AndroidHardwareBuffer id mismatch happened"; return; } mBuffers.erase(it); } already_AddRefed AndroidHardwareBufferManager::GetBuffer( uint64_t aBufferId) { MonitorAutoLock lock(mMonitor); const auto it = mBuffers.find(aBufferId); if (it == mBuffers.end()) { return nullptr; } auto buffer = RefPtr(it->second); return buffer.forget(); } bool AndroidHardwareBufferManager::WaitForBufferOwnership( AndroidHardwareBuffer* aBuffer) { MonitorAutoLock lock(mMonitor); if (aBuffer->mTransactionId.isNothing()) { return true; } auto it = mWaitingNotifyNotUsed.find(aBuffer->mId); if (it == mWaitingNotifyNotUsed.end()) { return true; } const double waitWarningTimeoutMs = 300; const double maxTimeoutSec = 3; auto begin = TimeStamp::Now(); bool isWaiting = true; while (isWaiting) { TimeDuration timeout = TimeDuration::FromMilliseconds(waitWarningTimeoutMs); CVStatus status = mMonitor.Wait(timeout); if (status == CVStatus::Timeout) { gfxCriticalNoteOnce << "AndroidHardwareBuffer wait is slow"; } const auto it = mWaitingNotifyNotUsed.find(aBuffer->mId); if (it == mWaitingNotifyNotUsed.end()) { return true; } auto now = TimeStamp::Now(); if ((now - begin).ToSeconds() > maxTimeoutSec) { isWaiting = false; gfxCriticalNote << "AndroidHardwareBuffer wait timeout"; } } return false; } bool AndroidHardwareBufferManager::IsWaitingForBufferOwnership( AndroidHardwareBuffer* aBuffer) { MonitorAutoLock lock(mMonitor); if (aBuffer->mTransactionId.isNothing()) { return false; } auto it = mWaitingNotifyNotUsed.find(aBuffer->mId); if (it == mWaitingNotifyNotUsed.end()) { return false; } return true; } void AndroidHardwareBufferManager::HoldUntilNotifyNotUsed( uint64_t aBufferId, uint64_t aFwdTransactionId, bool aUsesImageBridge) { MOZ_ASSERT(NS_IsMainThread() || InImageBridgeChildThread()); const auto it = mBuffers.find(aBufferId); if (it == mBuffers.end()) { return; } auto buffer = RefPtr(it->second); MonitorAutoLock lock(mMonitor); buffer->SetLastFwdTransactionId(aFwdTransactionId, aUsesImageBridge, lock); mWaitingNotifyNotUsed.emplace(aBufferId, buffer); } void AndroidHardwareBufferManager::NotifyNotUsed(ipc::FileDescriptor&& aFenceFd, uint64_t aBufferId, uint64_t aTransactionId, bool aUsesImageBridge) { MOZ_ASSERT(InImageBridgeChildThread()); MonitorAutoLock lock(mMonitor); auto it = mWaitingNotifyNotUsed.find(aBufferId); if (it != mWaitingNotifyNotUsed.end()) { if (aTransactionId < it->second->GetLastFwdTransactionId(lock)) { // Released on host side, but client already requested newer use texture. return; } it->second->SetReleaseFence(std::move(aFenceFd), lock); mWaitingNotifyNotUsed.erase(it); mMonitor.NotifyAll(); } } } // namespace layers } // namespace mozilla