Android SurfaceFlinger——GraphicBuffer获取内存信息(三十一)

        上一篇文章介绍了 GraphicBuffer 初始化的 initWithSize() 函数中的申请内存流程,这里我们看一下另一个比较重要的函数,GraphicBufferMapper. getTransportSize 获取内存信息。该函数通常在需要了解缓冲区的实际内存占用情况时调用,例如在调试内存使用情况或优化性能时。

一、函数解析

        GraphicBufferMapper 的 getTransportSize 方法是用于获取一个 GraphicBuffer 实例的实际传输大小。这是因为图形缓冲区的实际大小可能由于对齐、填充等因素而大于其逻辑大小(即 width * height * 像素大小)。getTransportSize 方法返回的是实际用于传输的字节数,这对于理解缓冲区的内存占用以及在进行 DMA(直接内存访问)操作时特别有用。 

1、GraphicBufferMapper

源码位置:/frameworks/native/libs/ui/GraphicBufferMapper.cpp

void GraphicBufferMapper::getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts)
{
    mMapper->getTransportSize(handle, outTransportNumFds, outTransportNumInts);
}

源码位置:/frameworks/native/libs/ui/include/ui/GraphicBufferMapper.h

std::unique_ptr<const GrallocMapper> mMapper;

         可以看到这里的 mMapper 是 GrallocMapper,而 Gralloc4Mapper 继承 GrallocMapper。

2、Gralloc4

源码位置:/frameworks/native/libs/ui/include/ui/Gralloc4.h

#include <android/hardware/graphics/mapper/4.0/IMapper.h>

class Gralloc4Mapper : public GrallocMapper {
    ……
    sp<hardware::graphics::mapper::V4_0::IMapper> mMapper;
}

源码位置:/frameworks/native/libs/ui/Gralloc4.cpp

void Gralloc4Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
                                      uint32_t* outNumInts) const {
    *outNumFds = uint32_t(bufferHandle->numFds);
    *outNumInts = uint32_t(bufferHandle->numInts);

    Error error;
    auto buffer = const_cast<native_handle_t*>(bufferHandle);
    auto ret = mMapper->getTransportSize(buffer, [&](const auto& tmpError, const auto& tmpNumFds,
                const auto& tmpNumInts) {
                    error = tmpError;
                    if (error != Error::NONE) {
                        return;
                    }
                     *outNumFds = tmpNumFds;
                     *outNumInts = tmpNumInts;
    });

    error = (ret.isOk()) ? error : kTransactionError;

    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
}

          这里的 mMapper 是一个 4.0 版本的 IMapper 接口,而 GrallocMapper 又继承自 IMapper,所以这里调用的其实就是 GrallocMapper 中的 getTransportSize() 函数。

3、GrallocMapper

 源码位置:/hardware/google/gchips/gralloc4/src/4.x/GrallocMapper.h 

 class GrallocMapper : public IMapper

 源码位置:/hardware/google/gchips/gralloc4/src/4.x/GrallocMapper.cpp

Return<void> GrallocMapper::getTransportSize(void *buffer, getTransportSize_cb hidl_cb)
{
    common::getTransportSize(buffer, hidl_cb);
    return Void();
}

        这里有调用了通用类中的 getTransportSize() 函数。

4、Mapper

源码位置:hardware/google/gchips/GrallocHAL/src/hidl_common/Mapper.h

void getTransportSize(void *buffer, IMapper::getTransportSize_cb hidl_cb);

源码位置:hardware/google/gchips/GrallocHAL/src/hidl_common/Mapper.cpp

void getTransportSize(void* buffer, IMapper::getTransportSize_cb hidl_cb)
{
	/* 检查缓冲区注册状态,缓冲区必须是由Gralloc分配的 */
	buffer_handle_t bufferHandle = gRegisteredHandles->get(buffer);
	……

    // 验证缓冲区句柄
	if (private_handle_t::validate(bufferHandle) < 0)
	{
		MALI_GRALLOC_LOGE("Buffer %p is corrupted", buffer);
		hidl_cb(Error::BAD_BUFFER, -1, -1);
		return;
	}
    // 返回传输大小信息
	hidl_cb(Error::NONE, bufferHandle->numFds, bufferHandle->numInts);
}

        该函数是用于查询通过 Gralloc 分配的缓冲区的传输大小信息的,主要目的是为了在 HIDL 环境中获取缓冲区的文件描述符数量和整型元数据的数量,这对于跨进程共享缓冲区非常重要。

bufferHandle->numFds:表示缓冲区关联的文件描述符数量,这通常是共享内存区域的标识符。
bufferHandle->numInts:表示缓冲区关联的整型元数据数量,这些元数据可能包含了缓冲区的尺寸、格式等关键信息。

        其中 buffer_handle_t 是 Gralloc 中用于表示缓冲区句柄的类型,而 private_handle_t 是其具体实现之一,包含了缓冲区的详细信息,如文件描述符和元数据。他们都继承自 native_handle_t 。

private_handle_t

源码位置:/hardware/google/gchips/gralloc4/src/mali_gralloc_buffer4.h

#include <cutils/native_handle.h>

struct private_handle_t;

struct private_handle_t : public native_handle
{
    private_handle_t(
		int _flags,
		uint64_t _alloc_sizes[MAX_BUFFER_FDS],
		uint64_t _consumer_usage, uint64_t _producer_usage,
		int _fds[MAX_FDS], int _fd_count,
		int _req_format, uint64_t _alloc_format,
		int _width, int _height, int _stride,
		uint64_t _layer_count, plane_info_t _plane_info[MAX_PLANES])
	    : private_handle_t()
	{
		flags = _flags; // 标记位
		fd_count = _fd_count; // 文件描述符数量
		width = _width; // 缓冲区宽度
		height = _height; // 缓冲区高度
		req_format = _req_format; // 请求格式
		producer_usage = _producer_usage; // 生产者的使用模式
		consumer_usage = _consumer_usage; // 消费者的使用模式
		stride = _stride;
		alloc_format = _alloc_format; // 分配格式
		layer_count = _layer_count;
		version = sizeof(native_handle);
        // 设置文件描述符的数量
		set_numfds(fd_count);
		memcpy(plane_info, _plane_info, sizeof(plane_info_t) * MAX_PLANES);

        // fds和alloc_sizes数组的内存复制
		if (_fds)
			memcpy(fds, _fds, sizeof(fds));

		if (_alloc_sizes)
			memcpy(alloc_sizes, _alloc_sizes, sizeof(alloc_sizes));

		memset(bases, 0, sizeof(bases));
		memset(ion_handles, 0, sizeof(ion_handles));
	}
}

        可以看到这里定义了 private_handle_t 结构体,它是 native_handle 的一个子类,这意味着它继承了native_handle 的所有属性和功能,并添加了一些特定于图形缓冲区管理的成员变量。

native_handle

源码位置:/system/core/libcutils/include/cutils/native_handle.h

typedef struct native_handle
{
    int version;        /* sizeof(native_handle_t) */
    int numFds;         /* number of file-descriptors at &data[0] */
    int numInts;        /* number of ints at &data[numFds] */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-length-array"
#endif
    int data[0];        /* numFds + numInts ints */
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} native_handle_t;

        native_handle_t 结构体是 Android 系统中实现跨进程通信(IPC)的关键组件,特别是在需要传递文件描述符或共享资源的情况下。native_handle_t 结构体的设计充分考虑了跨进程通信的需求和现代编译器的特性,是 Android 系统中实现高效、安全的资源共享和通信的重要基石。

二、跨进程通信

        前面说过 GraphicBuffer 的数据太大,没有办法进行 Binder 通信,那么这里如何实现跨进程通信的呢?我们先去图元生产者的基类 IGraphicBufferProducer 的远程端。

1、IGraphicBufferProducer.cpp

源码位置:/frameworks/native/libs/gui/IGraphicBufferProducer.cpp

class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
public:
    explicit BpGraphicBufferProducer(const sp<IBinder>& impl)
        : BpInterface<IGraphicBufferProducer>(impl)
    {
    }

    ~BpGraphicBufferProducer() override;

    virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
        Parcel data, reply;
        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
        data.writeInt32(bufferIdx);
        status_t result = remote()->transact(REQUEST_BUFFER, data, &reply);
        if (result != NO_ERROR) {
            return result;
        }
        bool nonNull = reply.readInt32();
        if (nonNull) {
            *buf = new GraphicBuffer();
            result = reply.read(**buf);
            if(result != NO_ERROR) {
                (*buf).clear();
                return result;
            }
        }
        result = reply.readInt32();
        return result;
    }
    ……
}

        其实就是在 App 进程中创建 GraphicBuffer 对象,但是这个对象通常不会立即向 ION(内存分配器)申请内存。而是调用了read 的方法,继续解压缩 reply 返回的数据包。因为 GraphicBuffer 是一个序列化(Flatten)对象,可以通过 flatten 方法转化为字节流,然后通过 Binder 通信发送。在接收端(App进程),通过 unflatten 方法从字节流中恢复 GraphicBuffer 对象。因此会走到 GraphicBuffer 的 unflatten 方法。

2、GraphicBuffer

源码位置:/frameworks/native/libs/ui/GraphicBuffer.cpp

status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) {
    ……
    int const* buf = static_cast<int const*>(buffer);

    ……
    if (numFds || numInts) {
        ……
        native_handle* h = native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
        ……
        memcpy(h->data, fds, numFds * sizeof(int));
        memcpy(h->data + numFds, buf + flattenWordCount, numInts * sizeof(int));
        handle = h;
    } else {
        ……
    }

    mId = static_cast<uint64_t>(buf[7]) << 32;
    mId |= static_cast<uint32_t>(buf[8]);

    mGenerationNumber = static_cast<uint32_t>(buf[9]);

    mOwner = ownHandle;

    if (handle != nullptr) {
        buffer_handle_t importedHandle;
        status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height),
                uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle);
        ……

        native_handle_close(handle);
        native_handle_delete(const_cast<native_handle_t*>(handle));
        handle = importedHandle;
        mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
    }

    buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
    size -= sizeNeeded;
    fds += numFds;
    count -= numFds;
    return NO_ERROR;
}

        这里获得 buffer_handle_t 句柄,接着调用 importBuffer 方法将这个句柄进一步转换为 private_handle_t。此时一般是 deqeue 方法中调用,GraphicBuffer 对象被从队列中取出,但还没有调用 lock 方法,因此 App 进程尚未直接访问共享内存,我们来看看 Mapper 的 lock() 方法。

3、Mapper

源码位置:/hardware/google/gchips/gralloc4/src/hidl_common/Mapper.cpp

void lock(void* buffer, uint64_t cpuUsage, const IMapper::Rect& accessRegion,
          const hidl_handle& acquireFence, IMapper::lock_cb hidl_cb)
{
	buffer_handle_t bufferHandle = gRegisteredHandles->get(buffer);
	if (!bufferHandle || private_handle_t::validate(bufferHandle) < 0)
	{
		MALI_GRALLOC_LOGE("Buffer to lock: %p is not valid", buffer);
		hidl_cb(Error::BAD_BUFFER, nullptr);
		return;
	}

	int fenceFd;
	if (!getFenceFd(acquireFence, &fenceFd))
	{
		hidl_cb(Error::BAD_VALUE, nullptr);
		return;
	}

	void* data = nullptr;
	const Error error = lockBuffer(bufferHandle, cpuUsage, accessRegion, fenceFd, &data);

	hidl_cb(error, data);
}

        lock方法是GraphicBuffer与共享内存关联的关键。在进行 mmap 共享内存绑定之前,会先查找已经在缓存的 buffer_handle_t 句柄,这个句柄关联了 App 进程和 SurfaceFlinger 进程中的 GraphicBuffer。通过 lock,App 进程可以将共享内存映射到自己的地址空间,从而直接访问图像数据。

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c小旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值