blob: 8a05abd9251c10d9458bb0116a24b1747f37733d [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/unique_fd.h>
#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
#include <hidl/LegacySupport.h>
#include <qemu_pipe_bp.h>
#include "glUtils.h"
#include "cb_handle_30.h"
#include "host_connection_session.h"
#include "types.h"
#include "debug.h"
const int kOMX_COLOR_FormatYUV420Planar = 19;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_vec;
using ::android::hardware::hidl_bitfield;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::graphics::common::V1_2::PixelFormat;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
namespace AllocatorV3 = ::android::hardware::graphics::allocator::V3_0;
namespace MapperV3 = ::android::hardware::graphics::mapper::V3_0;
using IAllocator3 = AllocatorV3::IAllocator;
using IMapper3 = MapperV3::IMapper;
using Error3 = MapperV3::Error;
using BufferDescriptorInfo = IMapper3::BufferDescriptorInfo;
namespace {
bool needGpuBuffer(const uint32_t usage) {
return usage & (BufferUsage::GPU_TEXTURE
| BufferUsage::GPU_RENDER_TARGET
| BufferUsage::COMPOSER_OVERLAY
| BufferUsage::COMPOSER_CLIENT_TARGET
| BufferUsage::GPU_DATA_BUFFER);
}
} // namespace
class GoldfishAllocator : public IAllocator3 {
public:
GoldfishAllocator() : m_hostConn(HostConnection::createUnique()) {}
Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
hidl_cb("GoldfishAllocator::dumpDebugInfo is not implemented");
return {};
}
Return<void> allocate(const hidl_vec<uint32_t>& rawDescriptor,
uint32_t count,
allocate_cb hidl_cb) {
uint32_t stride = 0;
std::vector<cb_handle_30_t*> cbs;
cbs.reserve(count);
const Error3 e = allocateImpl(rawDescriptor, count, &stride, &cbs);
if (e == Error3::NONE) {
hidl_vec<hidl_handle> handles(cbs.cbegin(), cbs.cend());
hidl_cb(Error3::NONE, stride, handles);
} else {
hidl_cb(e, 0, {});
}
for (cb_handle_30_t* cb : cbs) {
freeCb(std::unique_ptr<cb_handle_30_t>(cb));
}
return {};
}
private:
// this function should be in sync with GoldfishMapper::isSupportedImpl
Error3 allocateImpl(const hidl_vec<uint32_t>& rawDescriptor,
uint32_t count,
uint32_t* pStride,
std::vector<cb_handle_30_t*>* cbs) {
BufferDescriptorInfo descriptor;
if (!decodeBufferDescriptorInfo(rawDescriptor, &descriptor)) {
RETURN_ERROR(Error3::BAD_DESCRIPTOR);
}
if (!descriptor.width) { RETURN_ERROR(Error3::UNSUPPORTED); }
if (!descriptor.height) { RETURN_ERROR(Error3::UNSUPPORTED); }
if (descriptor.layerCount != 1) { RETURN_ERROR(Error3::UNSUPPORTED); }
const uint32_t usage = descriptor.usage;
int bpp = 1;
int glFormat = 0;
int glType = 0;
int align = 1;
bool yuv_format = false;
EmulatorFrameworkFormat emulatorFrameworkFormat =
EmulatorFrameworkFormat::GL_COMPATIBLE;
PixelFormat format;
Error3 e = getBufferFormat(descriptor.format, usage, &format);
if (e != Error3::NONE) {
ALOGE("%s:%d Unsupported format: frameworkFormat=%d, usage=%x",
__func__, __LINE__, descriptor.format, usage);
return e;
}
switch (format) {
case PixelFormat::RGBA_8888:
case PixelFormat::RGBX_8888:
case PixelFormat::BGRA_8888:
bpp = 4;
glFormat = GL_RGBA;
glType = GL_UNSIGNED_BYTE;
break;
case PixelFormat::RGB_888:
if (needGpuBuffer(usage)) {
RETURN_ERROR(Error3::UNSUPPORTED);
}
bpp = 3;
glFormat = GL_RGB;
glType = GL_UNSIGNED_BYTE;
break;
case PixelFormat::RGB_565:
bpp = 2;
glFormat = GL_RGB565;
glType = GL_UNSIGNED_SHORT_5_6_5;
break;
case PixelFormat::RGBA_FP16:
bpp = 8;
glFormat = GL_RGBA16F;
glType = GL_HALF_FLOAT;
break;
case PixelFormat::RGBA_1010102:
bpp = 4;
glFormat = GL_RGB10_A2;
glType = GL_UNSIGNED_INT_2_10_10_10_REV;
break;
case PixelFormat::RAW16:
case PixelFormat::Y16:
if (needGpuBuffer(usage)) {
RETURN_ERROR(Error3::UNSUPPORTED);
}
bpp = 2;
align = 16 * bpp;
glFormat = GL_LUMINANCE;
glType = GL_UNSIGNED_SHORT;
break;
case PixelFormat::BLOB:
if (needGpuBuffer(usage)) {
RETURN_ERROR(Error3::UNSUPPORTED);
}
glFormat = GL_LUMINANCE;
glType = GL_UNSIGNED_BYTE;
break;
case PixelFormat::YCRCB_420_SP:
if (needGpuBuffer(usage)) {
RETURN_ERROR(Error3::UNSUPPORTED);
}
yuv_format = true;
break;
case PixelFormat::YV12:
align = 16;
yuv_format = true;
// We are going to use RGB8888 on the host for Vulkan
glFormat = GL_RGBA;
glType = GL_UNSIGNED_BYTE;
emulatorFrameworkFormat = EmulatorFrameworkFormat::YV12;
break;
case PixelFormat::YCBCR_420_888:
yuv_format = true;
// We are going to use RGBA 8888 on the host
glFormat = GL_RGBA;
glType = GL_UNSIGNED_BYTE;
emulatorFrameworkFormat = EmulatorFrameworkFormat::YUV_420_888;
break;
case PixelFormat::YCBCR_P010:
yuv_format = true;
glFormat = GL_RGBA;
glType = GL_UNSIGNED_BYTE;
bpp = 2;
break;
default:
ALOGE("%s:%d Unsupported format: format=%d, frameworkFormat=%d, usage=%x",
__func__, __LINE__, format, descriptor.format, usage);
RETURN_ERROR(Error3::UNSUPPORTED);
}
const uint32_t width = descriptor.width;
const uint32_t height = descriptor.height;
size_t bufferSize;
uint32_t stride;
if (usage & (BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK)) {
const size_t align1 = align - 1;
if (yuv_format) {
const size_t yStride = (width * bpp + align1) & ~align1;
const size_t uvStride = (yStride / 2 + align1) & ~align1;
const size_t uvHeight = height / 2;
bufferSize = yStride * height + 2 * (uvHeight * uvStride);
stride = yStride / bpp;
} else {
const size_t bpr = (width * bpp + align1) & ~align1;
bufferSize = bpr * height;
stride = bpr / bpp;
}
} else {
bufferSize = 0;
stride = 0;
}
*pStride = stride;
return allocateImpl2(usage,
width, height,
format, emulatorFrameworkFormat,
glFormat, glType,
bufferSize,
bpp, stride,
count, cbs);
}
Error3 allocateImpl2(const uint32_t usage,
const uint32_t width, const uint32_t height,
const PixelFormat format,
const EmulatorFrameworkFormat emulatorFrameworkFormat,
const int glFormat, const int glType,
const size_t bufferSize,
const uint32_t bytesPerPixel,
const uint32_t stride,
const uint32_t count,
std::vector<cb_handle_30_t*>* cbs) {
for (uint32_t i = 0; i < count; ++i) {
cb_handle_30_t* cb;
Error3 e = allocateCb(usage,
width, height,
format, emulatorFrameworkFormat,
glFormat, glType,
bufferSize,
bytesPerPixel, stride,
&cb);
if (e == Error3::NONE) {
cbs->push_back(cb);
} else {
return e;
}
}
RETURN(Error3::NONE);
}
// see GoldfishMapper::encodeBufferDescriptorInfo
static bool decodeBufferDescriptorInfo(const hidl_vec<uint32_t>& raw,
BufferDescriptorInfo* d) {
if (raw.size() == 5) {
d->width = raw[0];
d->height = raw[1];
d->layerCount = raw[2];
d->format = static_cast<PixelFormat>(raw[3]);
d->usage = raw[4];
RETURN(true);
} else {
RETURN_ERROR(false);
}
}
static Error3 getBufferFormat(const PixelFormat frameworkFormat,
const uint32_t usage,
PixelFormat* format) {
if (frameworkFormat == PixelFormat::IMPLEMENTATION_DEFINED) {
RETURN_ERROR(Error3::UNSUPPORTED);
} else if (static_cast<int>(frameworkFormat) == kOMX_COLOR_FormatYUV420Planar &&
(usage & BufferUsage::VIDEO_DECODER)) {
ALOGW("gralloc_alloc: Requested OMX_COLOR_FormatYUV420Planar, given "
"YCbCr_420_888, taking experimental path. "
"usage=%x", usage);
*format = PixelFormat::YCBCR_420_888;
RETURN(Error3::NONE);
} else {
*format = frameworkFormat;
RETURN(Error3::NONE);
}
}
Error3 allocateCb(const uint32_t usage,
const uint32_t width, const uint32_t height,
const PixelFormat format,
const EmulatorFrameworkFormat emulatorFrameworkFormat,
const int glFormat, const int glType,
const size_t bufferSize,
const int32_t bytesPerPixel,
const int32_t stride,
cb_handle_30_t** cb) {
const HostConnectionSession conn = getHostConnectionSession();
ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder();
CRASH_IF(!rcEnc, "conn.getRcEncoder() failed");
android::base::unique_fd cpuAlocatorFd;
GoldfishAddressSpaceBlock bufferBits;
if (bufferSize > 0) {
GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator(
rcEnc->featureInfo_const()->hasSharedSlotsHostMemoryAllocator);
if (!host_memory_allocator.is_opened()) {
RETURN_ERROR(Error3::NO_RESOURCES);
}
if (host_memory_allocator.hostMalloc(&bufferBits, bufferSize)) {
RETURN_ERROR(Error3::NO_RESOURCES);
}
cpuAlocatorFd.reset(host_memory_allocator.release());
}
uint32_t hostHandle = 0;
android::base::unique_fd hostHandleRefCountFd;
if (needGpuBuffer(usage)) {
hostHandleRefCountFd.reset(qemu_pipe_open("refcount"));
if (!hostHandleRefCountFd.ok()) {
RETURN_ERROR(Error3::NO_RESOURCES);
}
const GLenum allocFormat =
(PixelFormat::RGBX_8888 == format) ? GL_RGB : glFormat;
hostHandle = rcEnc->rcCreateColorBufferDMA(
rcEnc,
width, height,
allocFormat, static_cast<int>(emulatorFrameworkFormat));
if (!hostHandle) {
RETURN_ERROR(Error3::NO_RESOURCES);
}
if (qemu_pipe_write(hostHandleRefCountFd.get(),
&hostHandle,
sizeof(hostHandle)) != sizeof(hostHandle)) {
rcEnc->rcCloseColorBuffer(rcEnc, hostHandle);
RETURN_ERROR(Error3::NO_RESOURCES);
}
}
std::unique_ptr<cb_handle_30_t> handle =
std::make_unique<cb_handle_30_t>(
cpuAlocatorFd.release(),
hostHandleRefCountFd.release(),
hostHandle,
usage,
width,
height,
static_cast<int>(format),
glFormat,
glType,
bufferSize,
bufferBits.guestPtr(),
bufferBits.size(),
bufferBits.offset(),
bytesPerPixel,
stride);
bufferBits.release();
*cb = handle.release();
RETURN(Error3::NONE);
}
void freeCb(std::unique_ptr<cb_handle_30_t> cb) {
if (cb->hostHandleRefcountFdIndex >= 0) {
::close(cb->fds[cb->hostHandleRefcountFdIndex]);
}
if (cb->bufferFdIndex >= 0) {
GoldfishAddressSpaceBlock::memoryUnmap(cb->getBufferPtr(), cb->mmapedSize);
GoldfishAddressSpaceHostMemoryAllocator::closeHandle(cb->fds[cb->bufferFdIndex]);
}
}
HostConnectionSession getHostConnectionSession() const {
return HostConnectionSession(m_hostConn.get());
}
std::unique_ptr<HostConnection> m_hostConn;
};
int main(int, char**) {
using ::android::sp;
::android::hardware::configureRpcThreadpool(4, true /* callerWillJoin */);
sp<IAllocator3> allocator(new GoldfishAllocator());
if (allocator->registerAsService() != ::android::NO_ERROR) {
ALOGE("failed to register graphics IAllocator@3.0 service");
return -EINVAL;
}
ALOGI("graphics IAllocator@3.0 service is initialized");
::android::hardware::joinRpcThreadpool();
ALOGI("graphics IAllocator@3.0 service is terminating");
return 0;
}