blob: 34945a56afb039fde7cfdf9a07d162052187b487 [file] [log] [blame]
// Copyright 2024 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 "GrallocEmulated.h"
#include <cutils/log.h>
#include <optional>
#include <unordered_map>
#include "drm_fourcc.h"
namespace gfxstream {
namespace {
static constexpr int numFds = 0;
static constexpr int numInts = 1;
#define DRM_FORMAT_R8_BLOB fourcc_code('9', '9', '9', '9')
template <typename T, typename N>
T DivideRoundUp(T n, N divisor) {
const T div = static_cast<T>(divisor);
const T q = n / div;
return n % div == 0 ? q : q + 1;
}
template <typename T, typename N>
T Align(T number, N n) {
return DivideRoundUp(number, n) * n;
}
std::optional<uint32_t> GlFormatToDrmFormat(uint32_t glFormat) {
switch (glFormat) {
case kGlRGB:
return DRM_FORMAT_BGR888;
case kGlRGB565:
return DRM_FORMAT_BGR565;
case kGlRGBA:
return DRM_FORMAT_ABGR8888;
}
return std::nullopt;
}
std::optional<uint32_t> AhbToDrmFormat(uint32_t ahbFormat) {
switch (ahbFormat) {
case GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM:
return DRM_FORMAT_ABGR8888;
case GFXSTREAM_AHB_FORMAT_R8G8B8X8_UNORM:
return DRM_FORMAT_XBGR8888;
case GFXSTREAM_AHB_FORMAT_R8G8B8_UNORM:
return DRM_FORMAT_BGR888;
/*
* Confusingly, AHARDWAREBUFFER_FORMAT_RGB_565 is defined as:
*
* "16-bit packed format that has 5-bit R, 6-bit G, and 5-bit B components, in that
* order, from the most-sigfinicant bits to the least-significant bits."
*
* so the order of the components is intentionally not flipped between the pixel
* format and the DRM format.
*/
case GFXSTREAM_AHB_FORMAT_R5G6B5_UNORM:
return DRM_FORMAT_RGB565;
case GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM:
return DRM_FORMAT_ARGB8888;
case GFXSTREAM_AHB_FORMAT_BLOB:
return DRM_FORMAT_R8_BLOB;
case GFXSTREAM_AHB_FORMAT_R8_UNORM:
return DRM_FORMAT_R8;
case GFXSTREAM_AHB_FORMAT_YV12:
return DRM_FORMAT_YVU420;
case GFXSTREAM_AHB_FORMAT_R16G16B16A16_FLOAT:
return DRM_FORMAT_ABGR16161616F;
case GFXSTREAM_AHB_FORMAT_R10G10B10A2_UNORM:
return DRM_FORMAT_ABGR2101010;
case GFXSTREAM_AHB_FORMAT_Y8Cb8Cr8_420:
return DRM_FORMAT_NV12;
}
return std::nullopt;
}
struct DrmFormatPlaneInfo {
uint32_t horizontalSubsampling;
uint32_t verticalSubsampling;
uint32_t bytesPerPixel;
};
struct DrmFormatInfo {
uint32_t androidFormat;
uint32_t virglFormat;
bool isYuv;
uint32_t horizontalAlignmentPixels;
uint32_t verticalAlignmentPixels;
std::vector<DrmFormatPlaneInfo> planes;
};
const std::unordered_map<uint32_t, DrmFormatInfo>& GetDrmFormatInfoMap() {
static const auto* kFormatInfoMap = new std::unordered_map<uint32_t, DrmFormatInfo>({
{DRM_FORMAT_ABGR8888,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM,
.virglFormat = VIRGL_FORMAT_R8G8B8A8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 4,
},
},
}},
{DRM_FORMAT_ARGB8888,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM,
.virglFormat = VIRGL_FORMAT_B8G8R8A8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 4,
},
},
}},
{DRM_FORMAT_BGR888,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R8G8B8_UNORM,
.virglFormat = VIRGL_FORMAT_R8G8B8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 3,
},
},
}},
{DRM_FORMAT_BGR565,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R5G6B5_UNORM,
.virglFormat = VIRGL_FORMAT_B5G6R5_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 2,
},
},
}},
{DRM_FORMAT_R8,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R8_UNORM,
.virglFormat = VIRGL_FORMAT_R8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
},
}},
{DRM_FORMAT_R8_BLOB,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_BLOB,
.virglFormat = VIRGL_FORMAT_R8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
},
}},
{DRM_FORMAT_ABGR16161616F,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R16G16B16A16_FLOAT,
.virglFormat = VIRGL_FORMAT_R16G16B16A16_FLOAT,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 8,
},
},
}},
{DRM_FORMAT_ABGR2101010,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R10G10B10A2_UNORM,
.virglFormat = VIRGL_FORMAT_R10G10B10A2_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 4,
},
},
}},
{DRM_FORMAT_NV12,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_Y8Cb8Cr8_420,
.virglFormat = VIRGL_FORMAT_NV12,
.isYuv = true,
.horizontalAlignmentPixels = 2,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
{
.horizontalSubsampling = 2,
.verticalSubsampling = 2,
.bytesPerPixel = 2,
},
},
}},
{DRM_FORMAT_YVU420,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_YV12,
.virglFormat = VIRGL_FORMAT_YV12,
.isYuv = true,
.horizontalAlignmentPixels = 32,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
{
.horizontalSubsampling = 2,
.verticalSubsampling = 2,
.bytesPerPixel = 1,
},
{
.horizontalSubsampling = 2,
.verticalSubsampling = 2,
.bytesPerPixel = 1,
},
},
}},
});
return *kFormatInfoMap;
}
} // namespace
EmulatedAHardwareBuffer::EmulatedAHardwareBuffer(uint32_t width, uint32_t height,
uint32_t drmFormat, VirtGpuResourcePtr resource)
: mRefCount(1), mWidth(width), mHeight(height), mDrmFormat(drmFormat), mResource(resource) {}
EmulatedAHardwareBuffer::~EmulatedAHardwareBuffer() {}
uint32_t EmulatedAHardwareBuffer::getResourceId() const { return mResource->getResourceHandle(); }
uint32_t EmulatedAHardwareBuffer::getWidth() const { return mWidth; }
uint32_t EmulatedAHardwareBuffer::getHeight() const { return mHeight; }
int EmulatedAHardwareBuffer::getAndroidFormat() const {
const auto& formatInfosMap = GetDrmFormatInfoMap();
auto formatInfoIt = formatInfosMap.find(mDrmFormat);
if (formatInfoIt == formatInfosMap.end()) {
ALOGE("Unhandled DRM format:%u", mDrmFormat);
return -1;
}
const auto& formatInfo = formatInfoIt->second;
return formatInfo.androidFormat;
}
uint32_t EmulatedAHardwareBuffer::getDrmFormat() const { return mDrmFormat; }
AHardwareBuffer* EmulatedAHardwareBuffer::asAHardwareBuffer() {
return reinterpret_cast<AHardwareBuffer*>(this);
}
buffer_handle_t EmulatedAHardwareBuffer::asBufferHandle() {
return reinterpret_cast<buffer_handle_t>(this);
}
EGLClientBuffer EmulatedAHardwareBuffer::asEglClientBuffer() {
return reinterpret_cast<EGLClientBuffer>(this);
}
void EmulatedAHardwareBuffer::acquire() { ++mRefCount; }
void EmulatedAHardwareBuffer::release() {
--mRefCount;
if (mRefCount == 0) {
delete this;
}
}
int EmulatedAHardwareBuffer::lock(uint8_t** ptr) {
if (!mMapped) {
mMapped = mResource->createMapping();
if (!mMapped) {
ALOGE("Failed to lock EmulatedAHardwareBuffer: failed to create mapping.");
return -1;
}
mResource->transferFromHost(0, 0, mWidth, mHeight);
mResource->wait();
}
*ptr = (*mMapped)->asRawPtr();
return 0;
}
int EmulatedAHardwareBuffer::lockPlanes(std::vector<Gralloc::LockedPlane>* ahbPlanes) {
uint8_t* data = 0;
int ret = lock(&data);
if (ret) {
return ret;
}
const auto& formatInfosMap = GetDrmFormatInfoMap();
auto formatInfoIt = formatInfosMap.find(mDrmFormat);
if (formatInfoIt == formatInfosMap.end()) {
ALOGE("Failed to lock: failed to find format info for drm format:%u", mDrmFormat);
return -1;
}
const auto& formatInfo = formatInfoIt->second;
const uint32_t alignedWidth = Align(mWidth, formatInfo.horizontalAlignmentPixels);
const uint32_t alignedHeight = Align(mHeight, formatInfo.verticalAlignmentPixels);
uint32_t cumulativeSize = 0;
for (const DrmFormatPlaneInfo& planeInfo : formatInfo.planes) {
const uint32_t planeWidth = DivideRoundUp(alignedWidth, planeInfo.horizontalSubsampling);
const uint32_t planeHeight = DivideRoundUp(alignedHeight, planeInfo.verticalSubsampling);
const uint32_t planeBpp = planeInfo.bytesPerPixel;
const uint32_t planeStrideBytes = planeWidth * planeBpp;
const uint32_t planeSizeBytes = planeHeight * planeStrideBytes;
ahbPlanes->emplace_back(Gralloc::LockedPlane{
.data = data + cumulativeSize,
.pixelStrideBytes = planeBpp,
.rowStrideBytes = planeStrideBytes,
});
cumulativeSize += planeSizeBytes;
}
if (mDrmFormat == DRM_FORMAT_NV12) {
const auto& uPlane = (*ahbPlanes)[1];
auto vPlane = uPlane;
vPlane.data += 1;
ahbPlanes->push_back(vPlane);
} else if (mDrmFormat == DRM_FORMAT_YVU420) {
// Note: lockPlanes() always returns Y, then U, then V but YV12 is Y, then V, then U.
auto& plane1 = (*ahbPlanes)[1];
auto& plane2 = (*ahbPlanes)[2];
std::swap(plane1, plane2);
}
return 0;
}
int EmulatedAHardwareBuffer::unlock() {
if (!mMapped) {
ALOGE("Failed to unlock EmulatedAHardwareBuffer: never locked?");
return -1;
}
mResource->transferToHost(0, 0, mWidth, mHeight);
mResource->wait();
mMapped.reset();
return 0;
}
EmulatedGralloc::EmulatedGralloc() {}
uint32_t EmulatedGralloc::createColorBuffer(void*, int width, int height, uint32_t glFormat) {
auto drmFormat = GlFormatToDrmFormat(glFormat);
if (!drmFormat) {
ALOGE("Unhandled format");
return -1;
}
auto ahb = allocate(width, height, *drmFormat);
if (ahb == nullptr) {
return -1;
}
EmulatedAHardwareBuffer* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
mOwned.emplace_back(rahb);
return rahb->getResourceId();
}
int EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t ahbFormat, uint64_t usage,
AHardwareBuffer** outputAhb) {
(void)usage;
auto drmFormat = AhbToDrmFormat(ahbFormat);
if (!drmFormat) {
ALOGE("Unhandled AHB format:%u", ahbFormat);
return -1;
}
*outputAhb = allocate(width, height, *drmFormat);
if (*outputAhb == nullptr) {
return -1;
}
return 0;
}
AHardwareBuffer* EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t drmFormat) {
ALOGE("Allocating AHB w:%u, h:%u, format %u", width, height, drmFormat);
auto device = VirtGpuDevice::getInstance();
if (!device) {
ALOGE("Failed to allocate: no virtio gpu device.");
return nullptr;
}
const auto& formatInfosMap = GetDrmFormatInfoMap();
auto formatInfoIt = formatInfosMap.find(drmFormat);
if (formatInfoIt == formatInfosMap.end()) {
ALOGE("Failed to allocate: failed to find format info for drm format:%u", drmFormat);
return nullptr;
}
const auto& formatInfo = formatInfoIt->second;
const uint32_t alignedWidth = Align(width, formatInfo.horizontalAlignmentPixels);
const uint32_t alignedHeight = Align(height, formatInfo.verticalAlignmentPixels);
uint32_t stride = 0;
uint32_t size = 0;
for (uint32_t i = 0; i < formatInfo.planes.size(); i++) {
const DrmFormatPlaneInfo& planeInfo = formatInfo.planes[i];
const uint32_t planeWidth = DivideRoundUp(alignedWidth, planeInfo.horizontalSubsampling);
const uint32_t planeHeight = DivideRoundUp(alignedHeight, planeInfo.verticalSubsampling);
const uint32_t planeBpp = planeInfo.bytesPerPixel;
const uint32_t planeStrideBytes = planeWidth * planeBpp;
const uint32_t planeSizeBytes = planeHeight * planeStrideBytes;
size += planeSizeBytes;
if (i == 0) stride = planeStrideBytes;
}
const uint32_t bind = (drmFormat == DRM_FORMAT_R8_BLOB || drmFormat == DRM_FORMAT_NV12 ||
drmFormat == DRM_FORMAT_YVU420)
? VIRGL_BIND_LINEAR
: VIRGL_BIND_RENDER_TARGET;
auto resource = device->createResource(width, height, stride, size, formatInfo.virglFormat,
PIPE_TEXTURE_2D, bind);
if (!resource) {
ALOGE("Failed to allocate: failed to create virtio resource.");
return nullptr;
}
resource->wait();
return reinterpret_cast<AHardwareBuffer*>(
new EmulatedAHardwareBuffer(width, height, drmFormat, std::move(resource)));
}
void EmulatedGralloc::acquire(AHardwareBuffer* ahb) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
rahb->acquire();
}
void EmulatedGralloc::release(AHardwareBuffer* ahb) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
rahb->release();
}
int EmulatedGralloc::lock(AHardwareBuffer* ahb, uint8_t** ptr) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
return rahb->lock(ptr);
}
int EmulatedGralloc::lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
return rahb->lockPlanes(ahbPlanes);
}
int EmulatedGralloc::unlock(AHardwareBuffer* ahb) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
return rahb->unlock();
}
uint32_t EmulatedGralloc::getHostHandle(const native_handle_t* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getResourceId();
}
uint32_t EmulatedGralloc::getHostHandle(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getResourceId();
}
const native_handle_t* EmulatedGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
return reinterpret_cast<const native_handle_t*>(ahb);
}
int EmulatedGralloc::getFormat(const native_handle_t* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getAndroidFormat();
}
int EmulatedGralloc::getFormat(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getAndroidFormat();
}
uint32_t EmulatedGralloc::getFormatDrmFourcc(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getDrmFormat();
}
uint32_t EmulatedGralloc::getWidth(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getWidth();
}
uint32_t EmulatedGralloc::getHeight(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getHeight();
}
size_t EmulatedGralloc::getAllocatedSize(const native_handle_t*) {
ALOGE("Unimplemented.");
return 0;
}
size_t EmulatedGralloc::getAllocatedSize(const AHardwareBuffer*) {
ALOGE("Unimplemented.");
return 0;
}
int EmulatedGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
const auto* rahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(ahb);
*id = rahb->getResourceId();
return 0;
}
Gralloc* createPlatformGralloc(int /*deviceFd*/) {
return new EmulatedGralloc();
}
} // namespace gfxstream