Android Surface Buffer尺寸逻辑分析

简介: 在AntGraphic实现canvas画布时需要支持动态修改画布buffer尺寸,刚开始以为网上有类似的资料,但查找一圈时并无收获,因此花时间研究了下Android平台的GraphicBuffer尺寸相关逻辑,记录如下一. BufferQueueCore中Buffer尺寸BufferQueueCore仅提供default尺寸,当Producer在未指定尺寸下申请buffer时,使用default尺

在AntGraphic实现canvas画布时需要支持动态修改画布buffer尺寸,刚开始以为网上有类似的资料,但查找一圈时并无收获,因此花时间研究了下Android平台的GraphicBuffer尺寸相关逻辑,记录如下

一. BufferQueueCore中Buffer尺寸

BufferQueueCore仅提供default尺寸,当Producer在未指定尺寸下申请buffer时,使用default尺寸生成对应的Buffer。

class BufferQueueCore : public virtual RefBase {
    
 	...
     
    // mDefaultWidth holds the default width of allocated buffers. It is used
    // in dequeueBuffer if a width and height of 0 are specified.
    uint32_t mDefaultWidth;

    // mDefaultHeight holds the default height of allocated buffers. It is used
    // in dequeueBuffer if a width and height of 0 are specified.
    uint32_t mDefaultHeight;
    
    ...
    
}

default尺寸初始化为1:

BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
    ...
    mMaxAcquiredBufferCount(1),
    mMaxDequeuedBufferCount(1),
   ...
{
   ...
}

BufferQueueCore不提供接口操作default尺寸,producer和consumer通过mCore直接访问default字段来操作。

二. Producer侧Buffer尺寸

Surface.h中涉及buffer尺寸的成员有:

class Surface { 	
    
	// mReqWidth is the buffer width that will be requested at the next dequeue
    // operation. It is initialized to 1.
    uint32_t mReqWidth;

    // mReqHeight is the buffer height that will be requested at the next
    // dequeue operation. It is initialized to 1.
    uint32_t mReqHeight;


	// mDefaultWidth is default width of the buffers, regardless of the
    // native_window_set_buffers_dimensions call.
    uint32_t mDefaultWidth;

    // mDefaultHeight is default height of the buffers, regardless of the
    // native_window_set_buffers_dimensions call.
    uint32_t mDefaultHeight;

	// mUserWidth, if non-zero, is an application-specified override
    // of mDefaultWidth.  This is lower priority than the width set by
    // native_window_set_buffers_dimensions.
    uint32_t mUserWidth;

    // mUserHeight, if non-zero, is an application-specified override
    // of mDefaultHeight.  This is lower priority than the height set
    // by native_window_set_buffers_dimensions.
    uint32_t mUserHeight;
   
    ...
}
 

包含三种尺寸,req、user、default。req、user共同控制producer dequeueBuffer时所采用的尺寸,而default尺寸不参与dequeueBuffer,只用来反映BufferQueue的默认尺寸。

我们先来看req、user尺寸。这两个尺寸的读写相关逻辑如下:

// 设置reqWidth / reqHeight 的逻辑
int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
{
    ATRACE_CALL();
    ALOGV("Surface::setBuffersDimensions");

    if ((width && !height) || (!width && height))
        return BAD_VALUE;

    Mutex::Autolock lock(mMutex);
    if (width != mReqWidth || height != mReqHeight) {
        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
    }
    mReqWidth = width;
    mReqHeight = height;
    return NO_ERROR;
}


// 设置userWidth / userHeight 的逻辑
int Surface::setBuffersUserDimensions(uint32_t width, uint32_t height)
{
    ATRACE_CALL();
    ALOGV("Surface::setBuffersUserDimensions");

    if ((width && !height) || (!width && height))
        return BAD_VALUE;

    Mutex::Autolock lock(mMutex);
    if (width != mUserWidth || height != mUserHeight) {
        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
    }
    mUserWidth = width;
    mUserHeight = height;
    return NO_ERROR;
}


// dequeueBuffer时尺寸逻辑
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::dequeueBuffer");

    uint32_t reqWidth;
    uint32_t reqHeight;
    PixelFormat reqFormat;
    uint32_t reqUsage;

    {
        Mutex::Autolock lock(mMutex);

        reqWidth = mReqWidth ? mReqWidth : mUserWidth;
        reqHeight = mReqHeight ? mReqHeight : mUserHeight;

        reqFormat = mReqFormat;
        reqUsage = mReqUsage;

        if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                BufferItem::INVALID_BUFFER_SLOT) {
            sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
            if (gbuf != NULL) {
                *buffer = gbuf.get();
                *fenceFd = -1;
                return OK;
            }
        }
    } // Drop the lock so that we can still touch the Surfa
    ...
}

从设置角度看,Surface提供setBuffersDimensions()和setBuffersUserDimensions()分别来设置req和user尺寸,但这两个接口并没有直接暴露给外部使用,而是通过perform()间接调用,通过传不同类型参数来确定哪个方法。涉及到的类型有:NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS。

前两个用来设置req尺寸,后一个用来设置user尺寸。NDK ANativeWindow接口提供接口设置user尺寸(不过命名有点混淆: ANativeWindow_setBuffersGeometry, 实际上改的是user尺寸): 

int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
        int32_t height, int32_t format) {
    int32_t err = native_window_set_buffers_format(window, format);
    if (!err) {
        err = native_window_set_buffers_user_dimensions(window, width, height);
        if (!err) {
            int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
            if (width && height) {
                mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
            }
            err = native_window_set_scaling_mode(window, mode);
         }
    }
    return err;
}

从使用角度看,这两个尺寸在Surface dequeueBuffer获取Buffer时用到,优先使用mReq尺寸,如mReq未设置则使用mUser尺寸。如果两者都为0,则在BufferQueueProducer实现中会取BufferQueue的默认尺寸做为需要的buffer尺寸:

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ALOGV("Surface::dequeueBuffer");

    uint32_t reqWidth;
    uint32_t reqHeight;
    PixelFormat reqFormat;
    uint32_t reqUsage;

    {
        Mutex::Autolock lock(mMutex);

        // 获取dequeueBuffer尺寸参数
        reqWidth = mReqWidth ? mReqWidth : mUserWidth;
        reqHeight = mReqHeight ? mReqHeight : mUserHeight;

        reqFormat = mReqFormat;
        reqUsage = mReqUsage;

        if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                BufferItem::INVALID_BUFFER_SLOT) {
            sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
            if (gbuf != NULL) {
                *buffer = gbuf.get();
                *fenceFd = -1;
                return OK;
            }
        }
    } 
    ... 
 }



status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
        sp<android::Fence> *outFence, uint32_t width, uint32_t height,
        PixelFormat format, uint32_t usage) {
    ...
    { // Autolock scope
        Mutex::Autolock lock(mCore->mMutex);
        mCore->waitWhileAllocatingLocked();

        if (format == 0) {
            format = mCore->mDefaultBufferFormat;
        }

        // Enable the usage bits the consumer requested
        usage |= mCore->mConsumerUsageBits;

        const bool useDefaultSize = !width && !height;
        if (useDefaultSize) {
            width = mCore->mDefaultWidth;
            height = mCore->mDefaultHeight;
        }
        ...
     }
    ...
}

那么,在EGL中查询EGLSurface的尺寸值是取的哪个值呢?看源码:

/**
 * eglQuerySurface查询尺寸会使用
 * type=NATIVE_WINDOW_DEFAULT_WIDTH/NATIVE_WINDOW_DEFAULT_HEIGHT
 */
int Surface::query(int what, int* value) const {
    ATRACE_CALL();
    ALOGV("Surface::query");
    { // scope for the lock
        Mutex::Autolock lock(mMutex);
        switch (what) {
            case NATIVE_WINDOW_FORMAT:
                if (mReqFormat) {
                    *value = static_cast<int>(mReqFormat);
                    return NO_ERROR;
                }
                break;
            case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
                sp<ISurfaceComposer> composer(
                        ComposerService::getComposerService());
                if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
                    *value = 1;
                } else {
                    *value = 0;
                }
                return NO_ERROR;
            }
            case NATIVE_WINDOW_CONCRETE_TYPE:
                *value = NATIVE_WINDOW_SURFACE;
                return NO_ERROR;
            case NATIVE_WINDOW_DEFAULT_WIDTH:
                *value = static_cast<int>(
                        mUserWidth ? mUserWidth : mDefaultWidth);
                return NO_ERROR;
            case NATIVE_WINDOW_DEFAULT_HEIGHT:
                *value = static_cast<int>(
                        mUserHeight ? mUserHeight : mDefaultHeight);
                return NO_ERROR;
            case NATIVE_WINDOW_TRANSFORM_HINT:
                *value = static_cast<int>(mTransformHint);
                return NO_ERROR;
            case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
                status_t err = NO_ERROR;
                if (!mConsumerRunningBehind) {
                    *value = 0;
                } else {
                    err = mGraphicBufferProducer->query(what, value);
                    if (err == NO_ERROR) {
                        mConsumerRunningBehind = *value;
                    }
                }
                return err;
            }
            case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: {
                int64_t durationUs = mLastDequeueDuration / 1000;
                *value = durationUs > std::numeric_limits<int>::max() ?
                        std::numeric_limits<int>::max() :
                        static_cast<int>(durationUs);
                return NO_ERROR;
            }
            case NATIVE_WINDOW_LAST_QUEUE_DURATION: {
                int64_t durationUs = mLastQueueDuration / 1000;
                *value = durationUs > std::numeric_limits<int>::max() ?
                        std::numeric_limits<int>::max() :
                        static_cast<int>(durationUs);
                return NO_ERROR;
            }
        }
    }
    return mGraphicBufferProducer->query(what, value);
}

从上面源码可知,当producer设置过user尺寸时,egl返回设置的user尺寸,否则返回default尺寸

三. Consumer侧Buffer尺寸

consumer侧比较简单,  仅提供设置BufferQueueCore default尺寸的接口,消费Buffer时根据Buffer本身尺寸操作,不做额外的校验等逻辑。 只有mDefaultWidth/mDefaultHeight尺寸,而且与BufferQueue的尺寸对应。consumer提供setDefaultBufferSize()接口修改default尺寸:

status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
        uint32_t height) {
    ATRACE_CALL();

    if (width == 0 || height == 0) {
        BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
                "height=%u)", width, height);
        return BAD_VALUE;
    }

    BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);

    Mutex::Autolock lock(mCore->mMutex);
    mCore->mDefaultWidth = width;
    mCore->mDefaultHeight = height;
    return NO_ERROR;
}

外部调用setDefaultBufferSize()的地方重点关注两处:

1.SurfaceFlinger的Layer setBuffer(),这个是Android标准UI流程,在Activity Window对应的Layer初始化时会触发Layer setBuffer();

2.TextureView中surfaceTexture的setDefaultBufferSize(),当TextureView初始化或view大小变化时会触发;

四.总结

从BufferQueue角度看,Producer和Consumer都可以修改Buffer大小,效果基本一致。对于上层Android UI框架,默认Activity的Window(Surface)由系统控制,Buffer大小和Activity可视大小一致,应用开发无法修改;TextureView Buffer尺寸默认与View大小一致,应用开发可以通过surfaceTexture的setDefaultBufferSize()或者NDK的ANativeWindow接口修改Buffer大小(两者效果一致),可以与View大小不一样,渲染时会自动scale;而SurfaceView有独立的Surface, consumer在SF内,只能通过Producer侧修改,不过SF consumer内会检查surface尺寸是否与Layer一致,如果不一样会reject,目前还没调研到应用层修改的方法(有待进一步再调研)。

相关文章
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
43 2
|
4天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
29 15
Android 系统缓存扫描与清理方法分析
|
19天前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
104 3
|
18天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
3月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
|
2月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
39 1
|
3月前
|
开发框架 Android开发 Swift
安卓与iOS应用开发对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文将深入探讨这两大操作系统在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的差异和特点。通过比较分析,旨在为开发者提供一个宏观的视角,帮助他们根据项目需求和目标受众选择最合适的开发平台。同时,文章还将讨论跨平台开发框架的利与弊,以及它们如何影响着移动应用的开发趋势。
|
3月前
|
安全 搜索推荐 Android开发
安卓与iOS应用开发的对比分析
【8月更文挑战第20天】在移动应用开发领域,安卓和iOS两大平台各领风骚。本文通过深入探讨两者的开发环境、编程语言、用户界面设计、应用市场及分发机制等方面的差异,揭示了各自的优势和挑战。旨在为开发者提供决策支持,同时帮助理解为何某些应用可能优先选择在一个平台上发布。
41 2