diff --git a/base/BUILD.gn b/base/BUILD.gn index 164d39f2f68602..1f94988635c5dc 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -137,6 +137,9 @@ jumbo_component("base") { "allocator/allocator_shim.h", "allocator/malloc_zone_functions_mac.cc", "allocator/malloc_zone_functions_mac.h", + "android/android_hardware_buffer_abi.h", + "android/android_hardware_buffer_compat.cc", + "android/android_hardware_buffer_compat.h", "android/animation_frame_time_histogram.cc", "android/apk_assets.cc", "android/apk_assets.h", @@ -509,6 +512,7 @@ jumbo_component("base") { "memory/shared_memory_android.cc", "memory/shared_memory_handle.cc", "memory/shared_memory_handle.h", + "memory/shared_memory_handle_android.cc", "memory/shared_memory_handle_mac.cc", "memory/shared_memory_handle_win.cc", "memory/shared_memory_helper.cc", @@ -1118,7 +1122,9 @@ jumbo_component("base") { } } - if (!is_mac && is_posix) { + # MacOS and Android have their own custom shared memory implementations due + # to supporting both POSIX and native handles. + if (is_posix && !is_mac && !is_android) { sources += [ "memory/shared_memory_handle_posix.cc" ] } diff --git a/base/android/android_hardware_buffer_abi.h b/base/android/android_hardware_buffer_abi.h new file mode 100644 index 00000000000000..c44ea9eba87395 --- /dev/null +++ b/base/android/android_hardware_buffer_abi.h @@ -0,0 +1,90 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_ +#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_ + +// Minimal binary interface definitions for AHardwareBuffer based on +// include/android/hardware_buffer.h from the Android NDK for platform level +// 26+. This is only intended for use from the AndroidHardwareBufferCompat +// wrapper for building without NDK platform level support, it is not a +// general-use header and is not complete. +// +// TODO(crbug.com/771171): Delete this file when third_party/android_tools/ndk/ +// is updated to a version that contains the android/hardware_buffer.h file. +// +// Please refer to the API documentation for details: +// https://developer.android.com/ndk/reference/hardware__buffer_8h.html + +#include + +// Use "C" linkage to match the original header file. This isn't strictly +// required since the file is not declaring global functions, but the types +// should remain in the global namespace for compatibility, and it's a reminder +// that forward declarations elsewhere should use "extern "C" to avoid +// namespace issues. +extern "C" { + +typedef struct AHardwareBuffer AHardwareBuffer; +typedef struct ARect ARect; + +enum { + AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1, + AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2, + AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3, + AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4, + AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 0x16, + AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b, + AHARDWAREBUFFER_FORMAT_BLOB = 0x21, +}; + +enum { + AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL, + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL, + AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL, + AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL, + AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4, + AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4, + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4, + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9, + AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, + AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, + AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, + AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, +}; + +typedef struct AHardwareBuffer_Desc { + uint32_t width; + uint32_t height; + uint32_t layers; + uint32_t format; + uint64_t usage; + uint32_t stride; + uint32_t rfu0; + uint64_t rfu1; +} AHardwareBuffer_Desc; + +using PFAHardwareBuffer_allocate = void (*)(const AHardwareBuffer_Desc* desc, + AHardwareBuffer** outBuffer); +using PFAHardwareBuffer_acquire = void (*)(AHardwareBuffer* buffer); +using PFAHardwareBuffer_describe = void (*)(const AHardwareBuffer* buffer, + AHardwareBuffer_Desc* outDesc); +using PFAHardwareBuffer_lock = int (*)(AHardwareBuffer* buffer, + uint64_t usage, + int32_t fence, + const ARect* rect, + void** outVirtualAddress); +using PFAHardwareBuffer_recvHandleFromUnixSocket = + int (*)(int socketFd, AHardwareBuffer** outBuffer); +using PFAHardwareBuffer_release = void (*)(AHardwareBuffer* buffer); +using PFAHardwareBuffer_sendHandleToUnixSocket = + int (*)(const AHardwareBuffer* buffer, int socketFd); +using PFAHardwareBuffer_unlock = int (*)(AHardwareBuffer* buffer, + int32_t* fence); + +} // extern "C" + +#endif // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_ diff --git a/base/android/android_hardware_buffer_compat.cc b/base/android/android_hardware_buffer_compat.cc new file mode 100644 index 00000000000000..70f058947bf13d --- /dev/null +++ b/base/android/android_hardware_buffer_compat.cc @@ -0,0 +1,129 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/android_hardware_buffer_compat.h" + +#include "base/android/build_info.h" +#include "base/lazy_instance.h" +#include "base/logging.h" + +#include + +namespace base { + +namespace { + +static base::LazyInstance::Leaky g_compat = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +AndroidHardwareBufferCompat::AndroidHardwareBufferCompat() { + DCHECK(IsSupportAvailable()); + + // TODO(klausw): If the Chromium build requires __ANDROID_API__ >= 26 at some + // point in the future, we could directly use the global functions instead of + // dynamic loading. However, since this would be incompatible with pre-Oreo + // devices, this is unlikely to happen in the foreseeable future, so just + // unconditionally use dynamic loading. + + // cf. base/android/linker/modern_linker_jni.cc + void* main_dl_handle = dlopen(nullptr, RTLD_NOW); + + *reinterpret_cast(&allocate_) = + dlsym(main_dl_handle, "AHardwareBuffer_allocate"); + DCHECK(allocate_); + + *reinterpret_cast(&acquire_) = + dlsym(main_dl_handle, "AHardwareBuffer_acquire"); + DCHECK(acquire_); + + *reinterpret_cast(&describe_) = + dlsym(main_dl_handle, "AHardwareBuffer_describe"); + DCHECK(describe_); + + *reinterpret_cast(&lock_) = + dlsym(main_dl_handle, "AHardwareBuffer_lock"); + DCHECK(lock_); + + *reinterpret_cast(&recv_handle_) = + dlsym(main_dl_handle, "AHardwareBuffer_recvHandleFromUnixSocket"); + DCHECK(recv_handle_); + + *reinterpret_cast(&release_) = + dlsym(main_dl_handle, "AHardwareBuffer_release"); + DCHECK(release_); + + *reinterpret_cast(&send_handle_) = + dlsym(main_dl_handle, "AHardwareBuffer_sendHandleToUnixSocket"); + DCHECK(send_handle_); + + *reinterpret_cast(&unlock_) = + dlsym(main_dl_handle, "AHardwareBuffer_unlock"); + DCHECK(unlock_); +} + +// static +bool AndroidHardwareBufferCompat::IsSupportAvailable() { + return base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_OREO; +} + +// static +AndroidHardwareBufferCompat AndroidHardwareBufferCompat::GetInstance() { + return g_compat.Get(); +} + +void AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc, + AHardwareBuffer** out_buffer) { + DCHECK(IsSupportAvailable()); + allocate_(desc, out_buffer); +} + +void AndroidHardwareBufferCompat::Acquire(AHardwareBuffer* buffer) { + DCHECK(IsSupportAvailable()); + acquire_(buffer); +} + +void AndroidHardwareBufferCompat::Describe(const AHardwareBuffer* buffer, + AHardwareBuffer_Desc* out_desc) { + DCHECK(IsSupportAvailable()); + describe_(buffer, out_desc); +} + +int AndroidHardwareBufferCompat::Lock(AHardwareBuffer* buffer, + uint64_t usage, + int32_t fence, + const ARect* rect, + void** out_virtual_address) { + DCHECK(IsSupportAvailable()); + return lock_(buffer, usage, fence, rect, out_virtual_address); +} + +int AndroidHardwareBufferCompat::RecvHandleFromUnixSocket( + int socket_fd, + AHardwareBuffer** out_buffer) { + DCHECK(IsSupportAvailable()); + return recv_handle_(socket_fd, out_buffer); +} + +void AndroidHardwareBufferCompat::Release(AHardwareBuffer* buffer) { + DCHECK(IsSupportAvailable()); + release_(buffer); +} + +int AndroidHardwareBufferCompat::SendHandleToUnixSocket( + const AHardwareBuffer* buffer, + int socket_fd) { + DCHECK(IsSupportAvailable()); + return send_handle_(buffer, socket_fd); +} + +int AndroidHardwareBufferCompat::Unlock(AHardwareBuffer* buffer, + int32_t* fence) { + DCHECK(IsSupportAvailable()); + return unlock_(buffer, fence); +} + +} // namespace base diff --git a/base/android/android_hardware_buffer_compat.h b/base/android/android_hardware_buffer_compat.h new file mode 100644 index 00000000000000..14be3d5b9c8aa7 --- /dev/null +++ b/base/android/android_hardware_buffer_compat.h @@ -0,0 +1,51 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_ +#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_ + +#include "base/android/android_hardware_buffer_abi.h" +#include "base/base_export.h" +#include "base/lazy_instance.h" + +namespace base { + +// This class provides runtime support for working with AHardwareBuffer objects +// on Android O systems without requiring building for the Android O NDK level. +// Don't call GetInstance() unless IsSupportAvailable() returns true. +class BASE_EXPORT AndroidHardwareBufferCompat { + public: + static bool IsSupportAvailable(); + static AndroidHardwareBufferCompat GetInstance(); + + void Allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer); + void Acquire(AHardwareBuffer* buffer); + void Describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc); + int Lock(AHardwareBuffer* buffer, + uint64_t usage, + int32_t fence, + const ARect* rect, + void** out_virtual_address); + int RecvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer); + void Release(AHardwareBuffer* buffer); + int SendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd); + int Unlock(AHardwareBuffer* buffer, int32_t* fence); + + private: + friend struct base::LazyInstanceTraitsBase; + AndroidHardwareBufferCompat(); + + PFAHardwareBuffer_allocate allocate_; + PFAHardwareBuffer_acquire acquire_; + PFAHardwareBuffer_describe describe_; + PFAHardwareBuffer_lock lock_; + PFAHardwareBuffer_recvHandleFromUnixSocket recv_handle_; + PFAHardwareBuffer_release release_; + PFAHardwareBuffer_sendHandleToUnixSocket send_handle_; + PFAHardwareBuffer_unlock unlock_; +}; + +} // namespace base + +#endif // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_ diff --git a/base/android/build_info.h b/base/android/build_info.h index 163f70eb5aafc2..52aec78fee4246 100644 --- a/base/android/build_info.h +++ b/base/android/build_info.h @@ -29,7 +29,8 @@ enum SdkVersion { SDK_VERSION_LOLLIPOP_MR1 = 22, SDK_VERSION_MARSHMALLOW = 23, SDK_VERSION_NOUGAT = 24, - SDK_VERSION_NOUGAT_MR1 = 25 + SDK_VERSION_NOUGAT_MR1 = 25, + SDK_VERSION_OREO = 26, }; // BuildInfo is a singleton class that stores android build and device diff --git a/base/memory/shared_memory_handle.h b/base/memory/shared_memory_handle.h index f719aeb9b245dd..c4e140e612636a 100644 --- a/base/memory/shared_memory_handle.h +++ b/base/memory/shared_memory_handle.h @@ -24,6 +24,10 @@ #include "base/file_descriptor_posix.h" #endif +#if defined(OS_ANDROID) +extern "C" typedef struct AHardwareBuffer AHardwareBuffer; +#endif + namespace base { // SharedMemoryHandle is the smallest possible IPC-transportable "reference" to @@ -82,26 +86,6 @@ class BASE_EXPORT SharedMemoryHandle { MACH, }; - // Constructs a SharedMemoryHandle backed by the components of a - // FileDescriptor. The newly created instance has the same ownership semantics - // as base::FileDescriptor. This typically means that the SharedMemoryHandle - // takes ownership of the |fd| if |auto_close| is true. Unfortunately, it's - // common for existing code to make shallow copies of SharedMemoryHandle, and - // the one that is finally passed into a base::SharedMemory is the one that - // "consumes" the fd. - // |guid| uniquely identifies the shared memory region pointed to by the - // underlying OS resource. If |file_descriptor| is associated with another - // SharedMemoryHandle, the caller must pass the |guid| of that - // SharedMemoryHandle. Otherwise, the caller should generate a new - // UnguessableToken. - // |size| refers to the size of the memory region pointed to by - // file_descriptor.fd. Passing the wrong |size| has no immediate consequence, - // but may cause errors when trying to map the SharedMemoryHandle at a later - // point in time. - SharedMemoryHandle(const base::FileDescriptor& file_descriptor, - size_t size, - const base::UnguessableToken& guid); - // Makes a Mach-based SharedMemoryHandle of the given size. On error, // subsequent calls to IsValid() return false. // Passing the wrong |size| has no immediate consequence, but may cause errors @@ -150,17 +134,6 @@ class BASE_EXPORT SharedMemoryHandle { SharedMemoryHandle(HANDLE h, size_t size, const base::UnguessableToken& guid); HANDLE GetHandle() const; #else - // |guid| uniquely identifies the shared memory region pointed to by the - // underlying OS resource. If |file_descriptor| is associated with another - // SharedMemoryHandle, the caller must pass the |guid| of that - // SharedMemoryHandle. Otherwise, the caller should generate a new - // UnguessableToken. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - SharedMemoryHandle(const base::FileDescriptor& file_descriptor, - size_t size, - const base::UnguessableToken& guid); - // Creates a SharedMemoryHandle from an |fd| supplied from an external // service. // Passing the wrong |size| has no immediate consequence, but may cause errors @@ -175,6 +148,54 @@ class BASE_EXPORT SharedMemoryHandle { int Release(); #endif +#if defined(OS_ANDROID) + enum class Type { + // The SharedMemoryHandle is not backed by a handle. This is used for + // a default-constructed SharedMemoryHandle() or for a failed duplicate. + // The other types are assumed to be valid. + INVALID, + // The SharedMemoryHandle is backed by a valid fd for ashmem. + ASHMEM, + // The SharedMemoryHandle is backed by a valid AHardwareBuffer object. + ANDROID_HARDWARE_BUFFER, + + LAST = ANDROID_HARDWARE_BUFFER + }; + Type GetType() const { return type_; } + SharedMemoryHandle(AHardwareBuffer* buffer, + size_t size, + const base::UnguessableToken& guid); + // Constructs a handle from file descriptor and type. Both ASHMEM and + // AHardwareBuffer types are transported via file descriptor for IPC, so the + // type field is needed to distinguish them. The generic file descriptor + // constructor below assumes type ASHMEM. + SharedMemoryHandle(Type type, + const base::FileDescriptor& file_descriptor, + size_t size, + const base::UnguessableToken& guid); + AHardwareBuffer* GetMemoryObject() const; +#endif + +#if defined(OS_POSIX) && !defined(OS_FUCHSIA) + // Constructs a SharedMemoryHandle backed by a FileDescriptor. The newly + // created instance has the same ownership semantics as base::FileDescriptor. + // This typically means that the SharedMemoryHandle takes ownership of the + // |fd| if |auto_close| is true. Unfortunately, it's common for existing code + // to make shallow copies of SharedMemoryHandle, and the one that is finally + // passed into a base::SharedMemory is the one that "consumes" the fd. + // + // |guid| uniquely identifies the shared memory region pointed to by the + // underlying OS resource. If |file_descriptor| is associated with another + // SharedMemoryHandle, the caller must pass the |guid| of that + // SharedMemoryHandle. Otherwise, the caller should generate a new + // UnguessableToken. + // Passing the wrong |size| has no immediate consequence, but may cause errors + // when trying to map the SharedMemoryHandle at a later point in time. + SharedMemoryHandle(const base::FileDescriptor& file_descriptor, + size_t size, + const base::UnguessableToken& guid); +#endif + private: #if defined(OS_MACOSX) && !defined(OS_IOS) friend class SharedMemory; @@ -196,6 +217,14 @@ class BASE_EXPORT SharedMemoryHandle { bool ownership_passes_to_ipc_ = false; }; }; +#elif defined(OS_ANDROID) + // Each instance of a SharedMemoryHandle is either INVALID, or backed by an + // ashmem fd, or backed by an AHardwareBuffer. |type_| determines the backing + // member. + Type type_ = Type::INVALID; + FileDescriptor file_descriptor_; + AHardwareBuffer* memory_object_ = nullptr; + bool ownership_passes_to_ipc_ = false; #elif defined(OS_FUCHSIA) zx_handle_t handle_ = ZX_HANDLE_INVALID; bool ownership_passes_to_ipc_ = false; diff --git a/base/memory/shared_memory_handle_android.cc b/base/memory/shared_memory_handle_android.cc new file mode 100644 index 00000000000000..e38d50e4132890 --- /dev/null +++ b/base/memory/shared_memory_handle_android.cc @@ -0,0 +1,187 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/shared_memory_handle.h" + +#include + +#include "base/android/android_hardware_buffer_compat.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/posix/unix_domain_socket.h" +#include "base/unguessable_token.h" + +namespace base { + +SharedMemoryHandle::SharedMemoryHandle() {} + +SharedMemoryHandle::SharedMemoryHandle( + const base::FileDescriptor& file_descriptor, + size_t size, + const base::UnguessableToken& guid) + : SharedMemoryHandle(Type::ASHMEM, file_descriptor, size, guid) {} + +SharedMemoryHandle::SharedMemoryHandle( + Type type, + const base::FileDescriptor& file_descriptor, + size_t size, + const base::UnguessableToken& guid) + : type_(type), guid_(guid), size_(size) { + switch (type) { + case Type::INVALID: + NOTREACHED() << "Can't create a Type::INVALID from a file descriptor"; + break; + case Type::ASHMEM: + DCHECK_GE(file_descriptor.fd, 0); + file_descriptor_ = file_descriptor; + break; + case Type::ANDROID_HARDWARE_BUFFER: + // This may be the first use of AHardwareBuffers in this process, so we + // need to load symbols. This should not fail since we're supposedly + // receiving one from IPC, but better to be paranoid. + if (!AndroidHardwareBufferCompat::IsSupportAvailable()) { + NOTREACHED() << "AHardwareBuffer support not available"; + type_ = Type::INVALID; + return; + } + + AHardwareBuffer* ahb = nullptr; + // A successful receive increments refcount, we don't need to do so + // separately. + int ret = + AndroidHardwareBufferCompat::GetInstance().RecvHandleFromUnixSocket( + file_descriptor.fd, &ahb); + + if (ret < 0) { + PLOG(ERROR) << "recv"; + type_ = Type::INVALID; + return; + } + + memory_object_ = ahb; + } +} + +SharedMemoryHandle::SharedMemoryHandle(AHardwareBuffer* buffer, + size_t size, + const base::UnguessableToken& guid) + : type_(Type::ANDROID_HARDWARE_BUFFER), + memory_object_(buffer), + ownership_passes_to_ipc_(false), + guid_(guid), + size_(size) {} + +// static +SharedMemoryHandle SharedMemoryHandle::ImportHandle(int fd, size_t size) { + SharedMemoryHandle handle; + handle.type_ = Type::ASHMEM; + handle.file_descriptor_.fd = fd; + handle.file_descriptor_.auto_close = false; + handle.guid_ = UnguessableToken::Create(); + handle.size_ = size; + return handle; +} + +int SharedMemoryHandle::GetHandle() const { + switch (type_) { + case Type::INVALID: + return -1; + case Type::ASHMEM: + DCHECK(IsValid()); + return file_descriptor_.fd; + case Type::ANDROID_HARDWARE_BUFFER: + DCHECK(IsValid()); + ScopedFD read_fd, write_fd; + CreateSocketPair(&read_fd, &write_fd); + + int ret = + AndroidHardwareBufferCompat::GetInstance().SendHandleToUnixSocket( + memory_object_, write_fd.get()); + + if (ret < 0) { + PLOG(ERROR) << "send"; + return -1; + } + + // Close write end now to avoid timeouts in case the receiver goes away. + write_fd.reset(); + + return read_fd.release(); + } +} + +bool SharedMemoryHandle::IsValid() const { + return type_ != Type::INVALID; +} + +void SharedMemoryHandle::Close() const { + switch (type_) { + case Type::INVALID: + return; + case Type::ASHMEM: + DCHECK(IsValid()); + if (IGNORE_EINTR(close(file_descriptor_.fd)) < 0) + PLOG(ERROR) << "close"; + break; + case Type::ANDROID_HARDWARE_BUFFER: + DCHECK(IsValid()); + AndroidHardwareBufferCompat::GetInstance().Release(memory_object_); + } +} + +int SharedMemoryHandle::Release() { + DCHECK_EQ(type_, Type::ASHMEM); + int old_fd = file_descriptor_.fd; + file_descriptor_.fd = -1; + return old_fd; +} + +SharedMemoryHandle SharedMemoryHandle::Duplicate() const { + switch (type_) { + case Type::INVALID: + return SharedMemoryHandle(); + case Type::ASHMEM: { + DCHECK(IsValid()); + int duped_handle = HANDLE_EINTR(dup(file_descriptor_.fd)); + if (duped_handle < 0) + return SharedMemoryHandle(); + return SharedMemoryHandle(FileDescriptor(duped_handle, true), GetSize(), + GetGUID()); + } + case Type::ANDROID_HARDWARE_BUFFER: + DCHECK(IsValid()); + AndroidHardwareBufferCompat::GetInstance().Acquire(memory_object_); + SharedMemoryHandle handle(*this); + handle.SetOwnershipPassesToIPC(true); + return handle; + } +} + +AHardwareBuffer* SharedMemoryHandle::GetMemoryObject() const { + DCHECK_EQ(type_, Type::ANDROID_HARDWARE_BUFFER); + return memory_object_; +} + +void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) { + switch (type_) { + case Type::ASHMEM: + file_descriptor_.auto_close = ownership_passes; + break; + case Type::INVALID: + case Type::ANDROID_HARDWARE_BUFFER: + ownership_passes_to_ipc_ = ownership_passes; + } +} + +bool SharedMemoryHandle::OwnershipPassesToIPC() const { + switch (type_) { + case Type::ASHMEM: + return file_descriptor_.auto_close; + case Type::INVALID: + case Type::ANDROID_HARDWARE_BUFFER: + return ownership_passes_to_ipc_; + } +} + +} // namespace base diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index c3f8843d222b8b..b67f718c98e7a3 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn @@ -352,6 +352,10 @@ test("gpu_unittests") { sources += [ "ipc/client/gpu_memory_buffer_impl_dxgi_unittest.cc" ] } + if (is_android) { + sources += [ "ipc/client/gpu_memory_buffer_impl_android_hardware_buffer_unittest.cc" ] + } + # TODO(geofflang): Run passthrough command decoder unittests on more platforms # once initialization of ANGLE's NULL context is supported if ((is_win || (is_linux && !use_ozone)) && !is_asan) { diff --git a/gpu/ipc/client/BUILD.gn b/gpu/ipc/client/BUILD.gn index ed85892e052398..1af50dfe5906f1 100644 --- a/gpu/ipc/client/BUILD.gn +++ b/gpu/ipc/client/BUILD.gn @@ -47,6 +47,12 @@ source_set("ipc_client_sources") { "gpu_memory_buffer_impl_native_pixmap.h", ] } + if (is_android) { + sources += [ + "gpu_memory_buffer_impl_android_hardware_buffer.cc", + "gpu_memory_buffer_impl_android_hardware_buffer.h", + ] + } configs += [ "//build/config/compiler:no_size_t_to_int_warning", "//gpu:gpu_implementation", diff --git a/gpu/ipc/client/gpu_memory_buffer_impl.cc b/gpu/ipc/client/gpu_memory_buffer_impl.cc index 73b18996eb7b75..c649c1fe9d8d05 100644 --- a/gpu/ipc/client/gpu_memory_buffer_impl.cc +++ b/gpu/ipc/client/gpu_memory_buffer_impl.cc @@ -20,6 +20,10 @@ #include "gpu/ipc/client/gpu_memory_buffer_impl_dxgi.h" #endif +#if defined(OS_ANDROID) +#include "gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h" +#endif + namespace gpu { GpuMemoryBufferImpl::GpuMemoryBufferImpl(gfx::GpuMemoryBufferId id, @@ -63,6 +67,11 @@ std::unique_ptr GpuMemoryBufferImpl::CreateFromHandle( case gfx::DXGI_SHARED_HANDLE: return GpuMemoryBufferImplDXGI::CreateFromHandle(handle, size, format, usage, callback); +#endif +#if defined(OS_ANDROID) + case gfx::ANDROID_HARDWARE_BUFFER: + return GpuMemoryBufferImplAndroidHardwareBuffer::CreateFromHandle( + handle, size, format, usage, callback); #endif default: NOTREACHED(); diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.cc b/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.cc new file mode 100644 index 00000000000000..d85a90bf4a66a8 --- /dev/null +++ b/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.cc @@ -0,0 +1,156 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h" + +#include + +#include "base/android/android_hardware_buffer_compat.h" +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/memory/shared_memory_handle.h" +#include "gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h" +#include "gpu/ipc/common/gpu_memory_buffer_support.h" +#include "ui/gfx/geometry/size.h" + +namespace gpu { + +namespace { + +AHardwareBuffer_Desc GetBufferDescription(const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage) { + // On create, all elements must be initialized, including setting the + // "reserved for future use" (rfu) fields to zero. + AHardwareBuffer_Desc desc = {}; + desc.width = size.width(); + desc.height = size.height(); + desc.layers = 1; // number of images + + switch (format) { + case gfx::BufferFormat::RGBA_8888: + desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + break; + case gfx::BufferFormat::RGBX_8888: + desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM; + break; + default: + NOTREACHED(); + } + + switch (usage) { + case gfx::BufferUsage::GPU_READ: + case gfx::BufferUsage::SCANOUT: + desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT; + break; + default: + NOTREACHED(); + } + return desc; +} + +} // namespace + +GpuMemoryBufferImplAndroidHardwareBuffer:: + GpuMemoryBufferImplAndroidHardwareBuffer( + gfx::GpuMemoryBufferId id, + const gfx::Size& size, + gfx::BufferFormat format, + const DestructionCallback& callback, + const base::SharedMemoryHandle& handle) + : GpuMemoryBufferImpl(id, size, format, callback), + shared_memory_(handle, false) {} + +GpuMemoryBufferImplAndroidHardwareBuffer:: + ~GpuMemoryBufferImplAndroidHardwareBuffer() {} + +// static +std::unique_ptr +GpuMemoryBufferImplAndroidHardwareBuffer::Create( + gfx::GpuMemoryBufferId id, + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + const DestructionCallback& callback) { + DCHECK(base::AndroidHardwareBufferCompat::IsSupportAvailable()); + + AHardwareBuffer* buffer = nullptr; + AHardwareBuffer_Desc desc = GetBufferDescription(size, format, usage); + base::AndroidHardwareBufferCompat::GetInstance().Allocate(&desc, &buffer); + if (!buffer) { + LOG(ERROR) << "Failed to allocate AHardwareBuffer"; + return nullptr; + } + + return base::WrapUnique(new GpuMemoryBufferImplAndroidHardwareBuffer( + id, size, format, callback, + base::SharedMemoryHandle(buffer, 0, base::UnguessableToken::Create()))); +} + +// static +std::unique_ptr +GpuMemoryBufferImplAndroidHardwareBuffer::CreateFromHandle( + const gfx::GpuMemoryBufferHandle& handle, + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + const DestructionCallback& callback) { + DCHECK(base::SharedMemory::IsHandleValid(handle.handle)); + return base::WrapUnique(new GpuMemoryBufferImplAndroidHardwareBuffer( + handle.id, size, format, callback, handle.handle)); +} + +// static +bool GpuMemoryBufferImplAndroidHardwareBuffer::IsConfigurationSupported( + gfx::BufferFormat format, + gfx::BufferUsage usage) { + return gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage); +} + +bool GpuMemoryBufferImplAndroidHardwareBuffer::Map() { + return false; +} + +void* GpuMemoryBufferImplAndroidHardwareBuffer::memory(size_t plane) { + return nullptr; +} + +void GpuMemoryBufferImplAndroidHardwareBuffer::Unmap() {} + +int GpuMemoryBufferImplAndroidHardwareBuffer::stride(size_t plane) const { + return 0; +} + +gfx::GpuMemoryBufferHandle GpuMemoryBufferImplAndroidHardwareBuffer::GetHandle() + const { + gfx::GpuMemoryBufferHandle handle; + handle.type = gfx::ANDROID_HARDWARE_BUFFER; + handle.id = id_; + handle.handle = shared_memory_.handle(); + + return handle; +} + +// static +base::Closure GpuMemoryBufferImplAndroidHardwareBuffer::AllocateForTesting( + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + gfx::GpuMemoryBufferHandle* handle) { + DCHECK(IsConfigurationSupported(format, usage)); + gfx::GpuMemoryBufferId kBufferId(1); + handle->type = gfx::ANDROID_HARDWARE_BUFFER; + handle->id = kBufferId; + AHardwareBuffer* buffer = nullptr; + AHardwareBuffer_Desc desc = GetBufferDescription(size, format, usage); + base::AndroidHardwareBufferCompat::GetInstance().Allocate(&desc, &buffer); + DCHECK(buffer); + handle->handle = + base::SharedMemoryHandle(buffer, 0, base::UnguessableToken::Create()); + return base::Bind(&base::DoNothing); +} + +} // namespace gpu diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h b/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h new file mode 100644 index 00000000000000..a0204293a75a44 --- /dev/null +++ b/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h @@ -0,0 +1,66 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GPU_IPC_CLIENT_GPU_MEMORY_BUFFER_IMPL_ANDROID_HARDWARE_BUFFER_H_ +#define GPU_IPC_CLIENT_GPU_MEMORY_BUFFER_IMPL_ANDROID_HARDWARE_BUFFER_H_ + +#include "base/memory/shared_memory.h" +#include "gpu/gpu_export.h" +#include "gpu/ipc/client/gpu_memory_buffer_impl.h" + +extern "C" typedef struct AHardwareBuffer AHardwareBuffer; + +namespace gpu { + +// Implementation of GPU memory buffer based on AHardwareBuffer. +class GPU_EXPORT GpuMemoryBufferImplAndroidHardwareBuffer + : public GpuMemoryBufferImpl { + public: + ~GpuMemoryBufferImplAndroidHardwareBuffer() override; + + static std::unique_ptr Create( + gfx::GpuMemoryBufferId id, + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + const DestructionCallback& callback); + + static std::unique_ptr + CreateFromHandle(const gfx::GpuMemoryBufferHandle& handle, + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + const DestructionCallback& callback); + + static bool IsConfigurationSupported(gfx::BufferFormat format, + gfx::BufferUsage usage); + + static base::Closure AllocateForTesting(const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + gfx::GpuMemoryBufferHandle* handle); + + // Overridden from gfx::GpuMemoryBuffer: + bool Map() override; + void* memory(size_t plane) override; + void Unmap() override; + int stride(size_t plane) const override; + gfx::GpuMemoryBufferHandle GetHandle() const override; + + private: + GpuMemoryBufferImplAndroidHardwareBuffer( + gfx::GpuMemoryBufferId id, + const gfx::Size& size, + gfx::BufferFormat format, + const DestructionCallback& callback, + const base::SharedMemoryHandle& handle); + + base::SharedMemory shared_memory_; + + DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferImplAndroidHardwareBuffer); +}; + +} // namespace gpu + +#endif // GPU_IPC_CLIENT_GPU_MEMORY_BUFFER_IMPL_ANDROID_HARDWARE_BUFFER_H_ diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer_unittest.cc b/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer_unittest.cc new file mode 100644 index 00000000000000..cb5cea7f01c26b --- /dev/null +++ b/gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer_unittest.cc @@ -0,0 +1,20 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gpu/ipc/client/gpu_memory_buffer_impl_android_hardware_buffer.h" +#include "gpu/ipc/client/gpu_memory_buffer_impl_test_template.h" + +namespace gpu { +namespace { + +INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferImplAndroidHardwareBuffer, + GpuMemoryBufferImplTest, + GpuMemoryBufferImplAndroidHardwareBuffer); + +INSTANTIATE_TYPED_TEST_CASE_P(GpuMemoryBufferImplAndroidHardwareBuffer, + GpuMemoryBufferImplCreateTest, + GpuMemoryBufferImplAndroidHardwareBuffer); + +} // namespace +} // namespace gpu diff --git a/gpu/ipc/common/gpu_memory_buffer_support.cc b/gpu/ipc/common/gpu_memory_buffer_support.cc index 7a3f3456078d73..8747c099224fc6 100644 --- a/gpu/ipc/common/gpu_memory_buffer_support.cc +++ b/gpu/ipc/common/gpu_memory_buffer_support.cc @@ -11,11 +11,17 @@ #include "ui/gfx/client_native_pixmap_factory.h" #endif +#if defined(OS_ANDROID) +#include "base/android/android_hardware_buffer_compat.h" +#endif + namespace gpu { gfx::GpuMemoryBufferType GetNativeGpuMemoryBufferType() { #if defined(OS_MACOSX) return gfx::IO_SURFACE_BUFFER; +#elif defined(OS_ANDROID) + return gfx::ANDROID_HARDWARE_BUFFER; #elif defined(OS_LINUX) return gfx::NATIVE_PIXMAP; #elif defined(OS_WIN) @@ -57,6 +63,24 @@ bool IsNativeGpuMemoryBufferConfigurationSupported(gfx::BufferFormat format, } NOTREACHED(); return false; +#elif defined(OS_ANDROID) + if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) { + return false; + } + switch (usage) { + case gfx::BufferUsage::GPU_READ: + case gfx::BufferUsage::SCANOUT: + return format == gfx::BufferFormat::RGBA_8888 || + format == gfx::BufferFormat::RGBX_8888; + case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE: + case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: + case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: + case gfx::BufferUsage::SCANOUT_VDA_WRITE: + case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: + return false; + } + NOTREACHED(); + return false; #elif defined(OS_LINUX) if (!gfx::ClientNativePixmapFactory::GetInstance()) { // unittests don't have to set ClientNativePixmapFactory. diff --git a/gpu/ipc/host/gpu_memory_buffer_support.cc b/gpu/ipc/host/gpu_memory_buffer_support.cc index 0a4e0c170db403..44cb946718c1d8 100644 --- a/gpu/ipc/host/gpu_memory_buffer_support.cc +++ b/gpu/ipc/host/gpu_memory_buffer_support.cc @@ -35,7 +35,8 @@ bool AreNativeGpuMemoryBuffersEnabled() { GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations() { GpuMemoryBufferConfigurationSet configurations; -#if defined(USE_OZONE) || defined(OS_MACOSX) || defined(OS_WIN) +#if defined(USE_OZONE) || defined(OS_MACOSX) || defined(OS_WIN) || \ + defined(OS_ANDROID) if (AreNativeGpuMemoryBuffersEnabled()) { const gfx::BufferFormat kNativeFormats[] = { gfx::BufferFormat::R_8, gfx::BufferFormat::RG_88, @@ -86,7 +87,8 @@ GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations() { uint32_t GetImageTextureTarget(gfx::BufferFormat format, gfx::BufferUsage usage) { -#if defined(USE_OZONE) || defined(OS_MACOSX) || defined(OS_WIN) +#if defined(USE_OZONE) || defined(OS_MACOSX) || defined(OS_WIN) || \ + defined(OS_ANDROID) GpuMemoryBufferConfigurationSet native_configurations = GetNativeGpuMemoryBufferConfigurations(); if (native_configurations.find(std::make_pair(format, usage)) == @@ -96,6 +98,7 @@ uint32_t GetImageTextureTarget(gfx::BufferFormat format, switch (GetNativeGpuMemoryBufferType()) { case gfx::NATIVE_PIXMAP: + case gfx::ANDROID_HARDWARE_BUFFER: // GPU memory buffers that are shared with the GL using EGLImages // require TEXTURE_EXTERNAL_OES. return GL_TEXTURE_EXTERNAL_OES; diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn index ffb0dee79d1719..b0e787866b1946 100644 --- a/gpu/ipc/service/BUILD.gn +++ b/gpu/ipc/service/BUILD.gn @@ -114,6 +114,8 @@ target(link_target_type, "ipc_service_sources") { } if (is_android) { sources += [ + "gpu_memory_buffer_factory_android_hardware_buffer.cc", + "gpu_memory_buffer_factory_android_hardware_buffer.h", "image_transport_surface_android.cc", "stream_texture_android.cc", "stream_texture_android.h", diff --git a/gpu/ipc/service/gpu_memory_buffer_factory.cc b/gpu/ipc/service/gpu_memory_buffer_factory.cc index 5adc710c235a8c..5a70a0221a5bac 100644 --- a/gpu/ipc/service/gpu_memory_buffer_factory.cc +++ b/gpu/ipc/service/gpu_memory_buffer_factory.cc @@ -20,6 +20,10 @@ #include "gpu/ipc/service/gpu_memory_buffer_factory_dxgi.h" #endif +#if defined(OS_ANDROID) +#include "gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.h" +#endif + namespace gpu { // static @@ -27,6 +31,8 @@ std::unique_ptr GpuMemoryBufferFactory::CreateNativeType() { #if defined(OS_MACOSX) return base::WrapUnique(new GpuMemoryBufferFactoryIOSurface); +#elif defined(OS_ANDROID) + return base::WrapUnique(new GpuMemoryBufferFactoryAndroidHardwareBuffer); #elif defined(OS_LINUX) return base::WrapUnique(new GpuMemoryBufferFactoryNativePixmap); #elif defined(OS_WIN) diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.cc b/gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.cc new file mode 100644 index 00000000000000..838a46daa5c726 --- /dev/null +++ b/gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.cc @@ -0,0 +1,82 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.h" + +#include "base/logging.h" +#include "base/memory/shared_memory_handle.h" +#include "build/build_config.h" +#include "ui/gl/gl_image_ahardwarebuffer.h" + +namespace gpu { + +GpuMemoryBufferFactoryAndroidHardwareBuffer:: + GpuMemoryBufferFactoryAndroidHardwareBuffer() {} + +GpuMemoryBufferFactoryAndroidHardwareBuffer:: + ~GpuMemoryBufferFactoryAndroidHardwareBuffer() {} + +gfx::GpuMemoryBufferHandle +GpuMemoryBufferFactoryAndroidHardwareBuffer::CreateGpuMemoryBuffer( + gfx::GpuMemoryBufferId id, + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + int client_id, + SurfaceHandle surface_handle) { + NOTIMPLEMENTED(); + return gfx::GpuMemoryBufferHandle(); +} + +void GpuMemoryBufferFactoryAndroidHardwareBuffer::DestroyGpuMemoryBuffer( + gfx::GpuMemoryBufferId id, + int client_id) {} + +ImageFactory* GpuMemoryBufferFactoryAndroidHardwareBuffer::AsImageFactory() { + return this; +} + +scoped_refptr +GpuMemoryBufferFactoryAndroidHardwareBuffer::CreateImageForGpuMemoryBuffer( + const gfx::GpuMemoryBufferHandle& handle, + const gfx::Size& size, + gfx::BufferFormat format, + unsigned internalformat, + int client_id, + SurfaceHandle surface_handle) { + // We should only end up in this code path if the memory buffer has a valid + // AHardwareBuffer. + DCHECK_EQ(handle.type, gfx::ANDROID_HARDWARE_BUFFER); + DCHECK_EQ(handle.handle.GetType(), + base::SharedMemoryHandle::Type::ANDROID_HARDWARE_BUFFER); + + AHardwareBuffer* buffer = handle.handle.GetMemoryObject(); + DCHECK(buffer); + + EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_FALSE, EGL_NONE}; + + scoped_refptr image(new gl::GLImageAHardwareBuffer(size)); + EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer); + if (!image->Initialize(EGL_NATIVE_BUFFER_ANDROID, client_buffer, attribs)) { + DLOG(ERROR) << "Failed to create GLImage " << size.ToString(); + return nullptr; + } + return image; +} + +scoped_refptr +GpuMemoryBufferFactoryAndroidHardwareBuffer::CreateAnonymousImage( + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + unsigned internalformat) { + NOTIMPLEMENTED(); + return nullptr; +} + +unsigned GpuMemoryBufferFactoryAndroidHardwareBuffer::RequiredTextureType() { + return GL_TEXTURE_EXTERNAL_OES; +} + +} // namespace gpu diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.h b/gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.h new file mode 100644 index 00000000000000..7cd7b614f96085 --- /dev/null +++ b/gpu/ipc/service/gpu_memory_buffer_factory_android_hardware_buffer.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GPU_IPC_SERVICE_GPU_MEMORY_BUFFER_FACTORY_ANDROID_HARDWARE_BUFFER_H_ +#define GPU_IPC_SERVICE_GPU_MEMORY_BUFFER_FACTORY_ANDROID_HARDWARE_BUFFER_H_ + +#include "gpu/command_buffer/service/image_factory.h" +#include "gpu/gpu_export.h" +#include "gpu/ipc/service/gpu_memory_buffer_factory.h" + +namespace gl { +class GLImage; +} + +namespace gpu { + +class GPU_EXPORT GpuMemoryBufferFactoryAndroidHardwareBuffer + : public GpuMemoryBufferFactory, + public ImageFactory { + public: + GpuMemoryBufferFactoryAndroidHardwareBuffer(); + ~GpuMemoryBufferFactoryAndroidHardwareBuffer() override; + + // Overridden from GpuMemoryBufferFactory: + gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer( + gfx::GpuMemoryBufferId id, + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + int client_id, + SurfaceHandle surface_handle) override; + void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id, + int client_id) override; + ImageFactory* AsImageFactory() override; + + // Overridden from ImageFactory: + scoped_refptr CreateImageForGpuMemoryBuffer( + const gfx::GpuMemoryBufferHandle& handle, + const gfx::Size& size, + gfx::BufferFormat format, + unsigned internalformat, + int client_id, + SurfaceHandle surface_handle) override; + scoped_refptr CreateAnonymousImage( + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + unsigned internalformat) override; + unsigned RequiredTextureType() override; + + private: + DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferFactoryAndroidHardwareBuffer); +}; + +} // namespace gpu + +#endif // GPU_IPC_SERVICE_GPU_MEMORY_BUFFER_FACTORY_ANDROID_HARDWARE_BUFFER_H_ diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc index 6ea3e902dcfd34..8d7319da9271ec 100644 --- a/ipc/ipc_message_utils.cc +++ b/ipc/ipc_message_utils.cc @@ -10,7 +10,6 @@ #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/memory/ptr_util.h" -#include "base/memory/shared_memory_handle.h" #include "base/strings/nullable_string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -568,6 +567,33 @@ void ParamTraits::Log(const param_type& p, } #endif // defined(OS_POSIX) +#if defined(OS_ANDROID) +void ParamTraits::Write(base::Pickle* m, + const param_type& p) { + DCHECK(static_cast(p) >= 0 && p <= base::SharedMemoryHandle::Type::LAST); + m->WriteInt(static_cast(p)); +} + +bool ParamTraits::Read( + const base::Pickle* m, + base::PickleIterator* iter, + param_type* r) { + int value; + if (!iter->ReadInt(&value)) + return false; + if (value < 0 || + value > static_cast(base::SharedMemoryHandle::Type::LAST)) + return false; + *r = static_cast(value); + return true; +} + +void ParamTraits::Log(const param_type& p, + std::string* l) { + l->append(base::IntToString(static_cast(p))); +} +#endif + void ParamTraits::Write(base::Pickle* m, const param_type& p) { // This serialization must be kept in sync with @@ -588,6 +614,12 @@ void ParamTraits::Write(base::Pickle* m, HandleFuchsia handle_fuchsia(p.GetHandle()); WriteParam(m, handle_fuchsia); #else +#if defined(OS_ANDROID) + // Android transfers both ashmem and AHardwareBuffer SharedMemoryHandle + // subtypes, both are transferred via file descriptor but need to be handled + // differently by the receiver. Write the type to distinguish. + WriteParam(m, p.GetType()); +#endif if (p.OwnershipPassesToIPC()) { if (!m->WriteAttachment(new internal::PlatformFileAttachment( base::ScopedFD(p.GetHandle())))) @@ -635,6 +667,13 @@ bool ParamTraits::Read(const base::Pickle* m, if (!ReadParam(m, iter, &handle_fuchsia)) return false; #else +#if defined(OS_ANDROID) + // Android uses both ashmen and AHardwareBuffer subtypes, get the actual type + // for use as a constructor argument alongside the file descriptor. + base::SharedMemoryHandle::Type android_subtype; + if (!ReadParam(m, iter, &android_subtype)) + return false; +#endif scoped_refptr attachment; if (!m->ReadAttachment(iter, &attachment)) return false; @@ -660,6 +699,14 @@ bool ParamTraits::Read(const base::Pickle* m, #elif defined(OS_FUCHSIA) *r = base::SharedMemoryHandle(handle_fuchsia.get_handle(), static_cast(size), guid); +#elif defined(OS_ANDROID) + *r = base::SharedMemoryHandle( + android_subtype, + base::FileDescriptor( + static_cast(attachment.get()) + ->TakePlatformFile(), + true), + static_cast(size), guid); #else *r = base::SharedMemoryHandle( base::FileDescriptor( diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 3d1b736e9977e1..54fee405190b5c 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -23,6 +23,7 @@ #include "base/containers/stack_container.h" #include "base/files/file.h" #include "base/format_macros.h" +#include "base/memory/shared_memory_handle.h" #include "base/numerics/safe_conversions.h" #include "base/optional.h" #include "base/strings/string16.h" @@ -39,7 +40,6 @@ class DictionaryValue; class FilePath; class ListValue; class NullableString16; -class SharedMemoryHandle; class Time; class TimeDelta; class TimeTicks; @@ -556,6 +556,18 @@ struct IPC_EXPORT ParamTraits { static void Log(const param_type& p, std::string* l); }; +#if defined(OS_ANDROID) +template <> +struct IPC_EXPORT ParamTraits { + typedef base::SharedMemoryHandle::Type param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; +#endif + #if defined(OS_WIN) template <> struct IPC_EXPORT ParamTraits { diff --git a/ui/gfx/gpu_memory_buffer.cc b/ui/gfx/gpu_memory_buffer.cc index 5c050207f0773a..813141df8ca639 100644 --- a/ui/gfx/gpu_memory_buffer.cc +++ b/ui/gfx/gpu_memory_buffer.cc @@ -42,6 +42,13 @@ GpuMemoryBufferHandle CloneHandleForIPC( #endif return handle; } + case gfx::ANDROID_HARDWARE_BUFFER: { + gfx::GpuMemoryBufferHandle handle; + handle.type = gfx::ANDROID_HARDWARE_BUFFER; + handle.id = source_handle.id; + handle.handle = base::SharedMemory::DuplicateHandle(source_handle.handle); + return handle; + } case gfx::IO_SURFACE_BUFFER: return source_handle; case gfx::DXGI_SHARED_HANDLE: diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h index 87ebea26b98c40..241dc78205871d 100644 --- a/ui/gfx/gpu_memory_buffer.h +++ b/ui/gfx/gpu_memory_buffer.h @@ -34,7 +34,8 @@ enum GpuMemoryBufferType { IO_SURFACE_BUFFER, NATIVE_PIXMAP, DXGI_SHARED_HANDLE, - GPU_MEMORY_BUFFER_TYPE_LAST = DXGI_SHARED_HANDLE + ANDROID_HARDWARE_BUFFER, + GPU_MEMORY_BUFFER_TYPE_LAST = ANDROID_HARDWARE_BUFFER }; using GpuMemoryBufferId = GenericSharedMemoryId; diff --git a/ui/gfx/mojo/buffer_types.mojom b/ui/gfx/mojo/buffer_types.mojom index 34d5c331912013..70a554858f8674 100644 --- a/ui/gfx/mojo/buffer_types.mojom +++ b/ui/gfx/mojo/buffer_types.mojom @@ -49,7 +49,7 @@ enum GpuMemoryBufferType { IO_SURFACE_BUFFER, NATIVE_PIXMAP, DXGI_SHARED_HANDLE, - LAST = DXGI_SHARED_HANDLE + ANDROID_HARDWARE_BUFFER, }; // gfx::GpuMemoryBufferId diff --git a/ui/gfx/mojo/buffer_types_struct_traits.cc b/ui/gfx/mojo/buffer_types_struct_traits.cc index 419bc2b46ba901..ddb3fd28942f5c 100644 --- a/ui/gfx/mojo/buffer_types_struct_traits.cc +++ b/ui/gfx/mojo/buffer_types_struct_traits.cc @@ -47,7 +47,8 @@ StructTraits:: shared_memory_handle(const gfx::GpuMemoryBufferHandle& handle) { if (handle.type != gfx::SHARED_MEMORY_BUFFER && - handle.type != gfx::DXGI_SHARED_HANDLE) + handle.type != gfx::DXGI_SHARED_HANDLE && + handle.type != gfx::ANDROID_HARDWARE_BUFFER) return mojo::ScopedSharedBufferHandle(); return mojo::WrapSharedMemoryHandle(handle.handle, handle.handle.GetSize(), false); @@ -85,7 +86,8 @@ bool StructTraitstype == gfx::SHARED_MEMORY_BUFFER || - out->type == gfx::DXGI_SHARED_HANDLE) { + out->type == gfx::DXGI_SHARED_HANDLE || + out->type == gfx::ANDROID_HARDWARE_BUFFER) { mojo::ScopedSharedBufferHandle handle = data.TakeSharedMemoryHandle(); if (handle.is_valid()) { MojoResult unwrap_result = mojo::UnwrapSharedMemoryHandle( diff --git a/ui/gfx/mojo/buffer_types_struct_traits.h b/ui/gfx/mojo/buffer_types_struct_traits.h index f3359bf0a5928c..bf1568d40ebeb0 100644 --- a/ui/gfx/mojo/buffer_types_struct_traits.h +++ b/ui/gfx/mojo/buffer_types_struct_traits.h @@ -190,9 +190,11 @@ struct EnumTraits { return gfx::mojom::GpuMemoryBufferType::NATIVE_PIXMAP; case gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE: return gfx::mojom::GpuMemoryBufferType::DXGI_SHARED_HANDLE; + case gfx::GpuMemoryBufferType::ANDROID_HARDWARE_BUFFER: + return gfx::mojom::GpuMemoryBufferType::ANDROID_HARDWARE_BUFFER; } NOTREACHED(); - return gfx::mojom::GpuMemoryBufferType::LAST; + return gfx::mojom::GpuMemoryBufferType::EMPTY_BUFFER; } static bool FromMojom(gfx::mojom::GpuMemoryBufferType input, @@ -213,6 +215,9 @@ struct EnumTraits { case gfx::mojom::GpuMemoryBufferType::DXGI_SHARED_HANDLE: *out = gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE; return true; + case gfx::mojom::GpuMemoryBufferType::ANDROID_HARDWARE_BUFFER: + *out = gfx::GpuMemoryBufferType::ANDROID_HARDWARE_BUFFER; + return true; } return false; } diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index 131bd3e7beac78..4e5028b7e9ed94 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn @@ -187,6 +187,13 @@ component("gl") { "gl_image_native_pixmap.h", ] } + + if (is_android) { + sources += [ + "gl_image_ahardwarebuffer.cc", + "gl_image_ahardwarebuffer.h", + ] + } } if (is_android || is_linux) { sources += [ @@ -421,6 +428,10 @@ test("gl_unittests") { ] } + if (is_android) { + sources += [ "gl_image_ahardwarebuffer_unittest.cc" ] + } + include_dirs = [ "//third_party/khronos" ] deps = [ diff --git a/ui/gl/gl_image_ahardwarebuffer.cc b/ui/gl/gl_image_ahardwarebuffer.cc new file mode 100644 index 00000000000000..d1377be935d158 --- /dev/null +++ b/ui/gl/gl_image_ahardwarebuffer.cc @@ -0,0 +1,46 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gl/gl_image_ahardwarebuffer.h" + +#include "ui/gl/gl_bindings.h" + +namespace gl { + +GLImageAHardwareBuffer::GLImageAHardwareBuffer(const gfx::Size& size) + : GLImageEGL(size) {} + +GLImageAHardwareBuffer::~GLImageAHardwareBuffer() {} + +unsigned GLImageAHardwareBuffer::GetInternalFormat() { + return GL_RGBA; +} + +bool GLImageAHardwareBuffer::CopyTexImage(unsigned target) { + return false; +} + +bool GLImageAHardwareBuffer::CopyTexSubImage(unsigned target, + const gfx::Point& offset, + const gfx::Rect& rect) { + return false; +} + +bool GLImageAHardwareBuffer::ScheduleOverlayPlane( + gfx::AcceleratedWidget widget, + int z_order, + gfx::OverlayTransform transform, + const gfx::Rect& bounds_rect, + const gfx::RectF& crop_rect) { + return false; +} + +void GLImageAHardwareBuffer::Flush() {} + +void GLImageAHardwareBuffer::OnMemoryDump( + base::trace_event::ProcessMemoryDump* pmd, + uint64_t process_tracing_id, + const std::string& dump_name) {} + +} // namespace gl diff --git a/ui/gl/gl_image_ahardwarebuffer.h b/ui/gl/gl_image_ahardwarebuffer.h new file mode 100644 index 00000000000000..a8ef6f9f19105f --- /dev/null +++ b/ui/gl/gl_image_ahardwarebuffer.h @@ -0,0 +1,44 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GL_GL_IMAGE_AHARDWAREBUFFER_H_ +#define UI_GL_GL_IMAGE_AHARDWAREBUFFER_H_ + +#include "base/macros.h" +#include "ui/gl/gl_export.h" +#include "ui/gl/gl_image_egl.h" + +namespace gl { + +class GL_EXPORT GLImageAHardwareBuffer : public GLImageEGL { + public: + explicit GLImageAHardwareBuffer(const gfx::Size& size); + + // Overridden from GLImage: + unsigned GetInternalFormat() override; + bool CopyTexImage(unsigned target) override; + bool CopyTexSubImage(unsigned target, + const gfx::Point& offset, + const gfx::Rect& rect) override; + bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget, + int z_order, + gfx::OverlayTransform transform, + const gfx::Rect& bounds_rect, + const gfx::RectF& crop_rect) override; + void SetColorSpace(const gfx::ColorSpace& color_space) override {} + void Flush() override; + void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, + uint64_t process_tracing_id, + const std::string& dump_name) override; + + protected: + ~GLImageAHardwareBuffer() override; + + private: + DISALLOW_COPY_AND_ASSIGN(GLImageAHardwareBuffer); +}; + +} // namespace gl + +#endif // UI_GL_GL_IMAGE_AHARDWAREBUFFER_H_ diff --git a/ui/gl/gl_image_ahardwarebuffer_unittest.cc b/ui/gl/gl_image_ahardwarebuffer_unittest.cc new file mode 100644 index 00000000000000..1332dd74f331b8 --- /dev/null +++ b/ui/gl/gl_image_ahardwarebuffer_unittest.cc @@ -0,0 +1,125 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include + +#include "base/android/android_hardware_buffer_compat.h" +#include "base/memory/scoped_refptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_image_ahardwarebuffer.h" +#include "ui/gl/test/gl_image_test_template.h" + +namespace gl { +namespace { + +const uint8_t kGreen[] = {0x0, 0xFF, 0x0, 0xFF}; + +template +class GLImageAHardwareBufferTestDelegate { + public: + scoped_refptr CreateSolidColorImage(const gfx::Size& size, + const uint8_t color[4]) const { + CHECK(base::AndroidHardwareBufferCompat::IsSupportAvailable()); + + // AHardwareBuffer_Desc's stride parameter is in pixels, not in bytes. + // + // We can't use the 3-byte-per-pixel AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM + // format in this test since there's no matching gfx::BufferFormat, + // gfx::BufferFormat::RGBX_8888 assumes 4 bytes per pixel. + const int kBytesPerPixel = 4; + + // cf. gpu_memory_buffer_impl_android_hardware_buffer + AHardwareBuffer_Desc desc = {}; + desc.width = size.width(); + desc.height = size.height(); + desc.layers = 1; // number of images + desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | + AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | + AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY; + + switch (format) { + case gfx::BufferFormat::RGBA_8888: + desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + break; + case gfx::BufferFormat::RGBX_8888: + desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM; + break; + default: + NOTREACHED(); + } + + AHardwareBuffer* buffer = nullptr; + base::AndroidHardwareBufferCompat::GetInstance().Allocate(&desc, &buffer); + EXPECT_TRUE(buffer); + + uint8_t* data = nullptr; + int lock_result = base::AndroidHardwareBufferCompat::GetInstance().Lock( + buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, nullptr, + reinterpret_cast(&data)); + EXPECT_EQ(lock_result, 0); + EXPECT_TRUE(data); + + AHardwareBuffer_Desc desc_locked = {}; + base::AndroidHardwareBufferCompat::GetInstance().Describe(buffer, + &desc_locked); + + GLImageTestSupport::SetBufferDataToColor( + size.width(), size.height(), desc_locked.stride * kBytesPerPixel, 0, + format, color, data); + + int unlock_result = base::AndroidHardwareBufferCompat::GetInstance().Unlock( + buffer, nullptr); + EXPECT_EQ(unlock_result, 0); + + auto image = base::MakeRefCounted(size); + EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; + EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer); + bool rv = + image->Initialize(EGL_NATIVE_BUFFER_ANDROID, client_buffer, attribs); + EXPECT_TRUE(rv); + return image; + } + + scoped_refptr CreateImage(const gfx::Size& size) const { + const uint8_t kTransparentBlack[4] = {0, 0, 0, 0}; + return CreateSolidColorImage(size, kTransparentBlack); + } + + unsigned GetTextureTarget() const { return GL_TEXTURE_2D; } + const uint8_t* GetImageColor() { return kGreen; } + int GetAdmissibleError() const { return 0; } +}; + +using GLImageTestTypes = testing::Types< + GLImageAHardwareBufferTestDelegate, + GLImageAHardwareBufferTestDelegate>; + +using GLImageRGBTestTypes = testing::Types< + GLImageAHardwareBufferTestDelegate>; + +// Disable the tests by default for now since they require Android O, +// the test bots don't generally have that yet. For manual testing, +// add: --gtest_also_run_disabled_tests -f 'GLImageAHardwareBuffer/*' + +INSTANTIATE_TYPED_TEST_CASE_P(DISABLED_GLImageAHardwareBuffer, + GLImageTest, + GLImageTestTypes); + +INSTANTIATE_TYPED_TEST_CASE_P(DISABLED_GLImageAHardwareBuffer, + GLImageOddSizeTest, + GLImageTestTypes); + +INSTANTIATE_TYPED_TEST_CASE_P(DISABLED_GLImageAHardwareBuffer, + GLImageBindTest, + GLImageTestTypes); + +INSTANTIATE_TYPED_TEST_CASE_P(DISABLED_GLImageAHardwareBuffer, + GLImageZeroInitializeTest, + GLImageRGBTestTypes); + +} // namespace +} // namespace gl diff --git a/ui/gl/gl_image_egl.h b/ui/gl/gl_image_egl.h index a913f41a0e78c3..4ebc3d7aff910c 100644 --- a/ui/gl/gl_image_egl.h +++ b/ui/gl/gl_image_egl.h @@ -13,6 +13,7 @@ namespace gl { +// Abstract base class for EGL-based images. class GL_EXPORT GLImageEGL : public GLImage { public: explicit GLImageEGL(const gfx::Size& size);