在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,目前还没调研到应用层修改的方法(有待进一步再调研)。