/* 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 "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" #include 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; } } static Maybe ToSurfaceFormat(uint32_t aFormat) { switch (aFormat) { case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: return Some(gfx::SurfaceFormat::R8G8B8A8); case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: return Some(gfx::SurfaceFormat::R8G8B8X8); case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: return Some(gfx::SurfaceFormat::R5G6B5_UINT16); default: return Nothing(); } } /* 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 (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; AHardwareBuffer_allocate(&desc, &nativeBuffer); if (!nativeBuffer) { return nullptr; } AHardwareBuffer_Desc bufferInfo = {}; AHardwareBuffer_describe(nativeBuffer, &bufferInfo); RefPtr buffer = new AndroidHardwareBuffer( nativeBuffer, aSize, bufferInfo.stride, aFormat); if (auto* manager = AndroidHardwareBufferManager::Get()) { manager->Register(buffer); } return buffer.forget(); } AndroidHardwareBuffer::AndroidHardwareBuffer(AHardwareBuffer* aNativeBuffer, gfx::IntSize aSize, uint32_t aStride, gfx::SurfaceFormat aFormat) : mSize(aSize), mStride(aStride), mFormat(aFormat), mId(GetNextId()), mNativeBuffer(aNativeBuffer), mIsRegistered(false) { MOZ_ASSERT(mNativeBuffer); #ifdef DEBUG AHardwareBuffer_Desc bufferInfo = {}; AHardwareBuffer_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); } AHardwareBuffer_release(mNativeBuffer); } UniqueFileHandle AndroidHardwareBuffer::SerializeToFileDescriptor() const { int fd[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fd) != 0) { gfxCriticalNote << "AndroidHardwareBuffer::SerializeToFileDescriptor: " "Failed to create Unix socket"; return UniqueFileHandle(); } UniqueFileHandle readerFd(fd[0]); UniqueFileHandle writerFd(fd[1]); const int ret = AHardwareBuffer_sendHandleToUnixSocket(mNativeBuffer, writerFd.get()); if (ret < 0) { gfxCriticalNote << "AndroidHardwareBuffer::SerializeToFileDescriptor: " "sendHandleToUnixSocket failed"; return UniqueFileHandle(); } return readerFd; } /* static */ already_AddRefed AndroidHardwareBuffer::DeserializeFromFileDescriptor(UniqueFileHandle&& aFd) { if (!aFd) { gfxCriticalNote << "AndroidHardwareBuffer::DeserializeFromFileDescriptor: " "Invalid FileDescriptor"; return nullptr; } AHardwareBuffer* nativeBuffer = nullptr; int ret = AHardwareBuffer_recvHandleFromUnixSocket(aFd.get(), &nativeBuffer); if (ret < 0) { gfxCriticalNote << "AndroidHardwareBuffer::DeserializeFromFileDescriptor: " "recvHandleFromUnixSocket failed"; return nullptr; } AHardwareBuffer_Desc desc = {}; AHardwareBuffer_describe(nativeBuffer, &desc); const auto format = ToSurfaceFormat(desc.format); if (!format) { gfxCriticalNote << "AndroidHardwareBuffer::DeserializeFromFileDescriptor: " "Unrecognized AHARDWAREBUFFER_FORMAT"; AHardwareBuffer_release(nativeBuffer); return nullptr; } RefPtr buffer = new AndroidHardwareBuffer( nativeBuffer, gfx::IntSize(desc.width, desc.height), desc.stride, *format); return buffer.forget(); } int AndroidHardwareBuffer::Lock(uint64_t aUsage, const ARect* aRect, void** aOutVirtualAddress) { UniqueFileHandle fd = GetAndResetReleaseFence(); return AHardwareBuffer_lock(mNativeBuffer, aUsage, fd.get(), aRect, aOutVirtualAddress); } int AndroidHardwareBuffer::Unlock() { int rawFd = -1; // XXX All tested recent Android devices did not return valid fence. int ret = AHardwareBuffer_unlock(mNativeBuffer, &rawFd); if (ret != 0) { return ret; } SetAcquireFence(UniqueFileHandle(rawFd)); return 0; } void AndroidHardwareBuffer::SetReleaseFence(UniqueFileHandle&& aFenceFd) { MonitorAutoLock lock(mMonitor); mReleaseFenceFd = std::move(aFenceFd); } void AndroidHardwareBuffer::SetAcquireFence(UniqueFileHandle&& aFenceFd) { MonitorAutoLock lock(mMonitor); mAcquireFenceFd = std::move(aFenceFd); } UniqueFileHandle AndroidHardwareBuffer::GetAndResetReleaseFence() { MonitorAutoLock lock(mMonitor); return std::move(mReleaseFenceFd); } UniqueFileHandle AndroidHardwareBuffer::GetAndResetAcquireFence() { MonitorAutoLock lock(mMonitor); return std::move(mAcquireFenceFd); } UniqueFileHandle AndroidHardwareBuffer::GetAcquireFence() const { MonitorAutoLock lock(mMonitor); if (!mAcquireFenceFd) { return UniqueFileHandle(); } return DuplicateFileHandle(mAcquireFenceFd); } StaticAutoPtr AndroidHardwareBufferManager::sInstance; /* static */ void AndroidHardwareBufferManager::Init() { MOZ_ASSERT(XRE_IsGPUProcess()); sInstance = new AndroidHardwareBufferManager(); } /* static */ void AndroidHardwareBufferManager::Shutdown() { sInstance = nullptr; } 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) const { MonitorAutoLock lock(mMonitor); const auto it = mBuffers.find(aBufferId); if (it == mBuffers.end()) { return nullptr; } auto buffer = RefPtr(it->second); return buffer.forget(); } } // namespace layers } // namespace mozilla