Android图形显示之硬件抽象层Gralloc(hal 转)
原文 http://blog.csdn.net/yangwen123/article/details/12192401
FrameBuffer驱动程序分析 文中介绍了Linux系统下的显示驱动框架,每个显示屏被抽象为一个帧缓冲区,注册到FrameBuffer模块中,并在/dev/graphics目录下创建对应的fbX设备。Android系统在硬件抽象层中提供了一个Gralloc模块,封装了对帧缓冲区的所有访问操作。用户空间的应用程序在使用帧缓冲区之间,首先要加载Gralloc模块,并且获得一个gralloc设备和一个fb设备。有了gralloc设备之后,用户空间中的应用程序就可以申请分配一块图形缓冲区,并且将这块图形缓冲区映射到应用程序的地址空间来,以便可以向里面写入要绘制的画面的内容。最后,用户空间中的应用程序就通过fb设备来将已经准备好了的图形缓冲区渲染到帧缓冲区中去,即将图形缓冲区的内容绘制到显示屏中去。相应地,当用户空间中的应用程序不再需要使用一块图形缓冲区的时候,就可以通过gralloc设备来释放它,并且将它从地址空间中解除映射。
Gralloc模块实现源码位于:hardware/libhardware/modules/gralloc
.
├── Android.mk
├── framebuffer.cpp
├── gralloc.cpp
├── gralloc_priv.h
├── gr.h
└── mapper.cpp
Android硬件抽象Hardware库加载过程源码分析 介绍了Android系统中的硬件抽象层模块的加载过程,并指出每个硬件抽象层模块都必须定义HAL_MODULE_INFO_SYM符号,并且有自己唯一的ID,Gralloc也不例外,Gralloc模块ID定义为:
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
同时定义了以HAL_MODULE_INFO_SYM为符号的类型为private_module_t的结构体:
hardware\libhardware\modules\gralloc\gralloc.cpp
static struct hw_module_methods_t gralloc_module_methods = {
open: gralloc_device_open
};
struct private_module_t HAL_MODULE_INFO_SYM = {
base: {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: GRALLOC_HARDWARE_MODULE_ID,
name: "Graphics Memory Allocator Module",
author: "The Android Open Source Project",
methods: &gralloc_module_methods
},
registerBuffer: gralloc_register_buffer,
unregisterBuffer: gralloc_unregister_buffer,
lock: gralloc_lock,
unlock: gralloc_unlock,
},
framebuffer: 0,
flags: 0,
numBuffers: 0,
bufferMask: 0,
lock: PTHREAD_MUTEX_INITIALIZER,
currentBuffer: 0,
};
通过 Android硬件抽象Hardware库加载过程源码分析 的方法将Gralloc模块加载到内存中来之后,就可以调用函数dlsym来获得它所导出的符号HMI,得到private_module_t的首地址后,由于private_module_t的第一个成员变量的类型为gralloc_module_t,因此也是gralloc_module_t的首地址,由于gralloc_module_t的第一个成员变量类型为hw_module_t,因此也是hw_module_t的首地址,因此只要得到这三种类型中其中一种类型变量的地址,就可以相互转换为其他两种类型的指针。
数据结构定义
在分析Gralloc模块之前,首先介绍Gralloc模块定义的一些数据结构。private_module_t用于描述Gralloc模块下的系统帧缓冲区信息
struct private_module_t {
gralloc_module_t base;
private_handle_t* framebuffer; //指向系统帧缓冲区的句柄
uint32_t flags; //用来标志系统帧缓冲区是否支持双缓冲
uint32_t numBuffers;//表示系统帧缓冲区包含有多少个图形缓冲区
uint32_t bufferMask; //记录系统帧缓冲区中的图形缓冲区的使用情况
pthread_mutex_t lock; //一个互斥锁,用来保护结构体private_module_t的并行访问
buffer_handle_t currentBuffer; //用来描述当前正在被渲染的图形缓冲区
int pmem_master;
void* pmem_master_base;
struct fb_var_screeninfo info; //保存设备显示屏的动态属性信息
struct fb_fix_screeninfo finfo; ////保存设备显示屏的固定属性信息
float xdpi; //描述设备显示屏在宽度
float ydpi; //描述设备显示屏在高度
float fps; //用来描述显示屏的刷新频率
};
framebuffer_device_t用来描述系统帧缓冲区设备的信息
typedef struct framebuffer_device_t {
struct hw_device_t common;
const uint32_t flags;//用来记录系统帧缓冲区的标志
const uint32_t width;//用来描述设备显示屏的宽度
const uint32_t height;//用来描述设备显示屏的高度
const int stride;//用来描述设备显示屏的一行有多少个像素点
const int format;//用来描述系统帧缓冲区的像素格式
const float xdpi;//用来描述设备显示屏在宽度上的密度
const float ydpi;//用来描述设备显示屏在高度上的密度
const float fps;//用来描述设备显示屏的刷新频率
const int minSwapInterval;//用来描述帧缓冲区交换前后两个图形缓冲区的最小时间间隔
const int maxSwapInterval;//用来描述帧缓冲区交换前后两个图形缓冲区的最大时间间隔
int reserved[8];//保留
//用来设置帧缓冲区交换前后两个图形缓冲区的最小和最大时间间隔
int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);
//用来设置帧缓冲区的更新区域
int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);
//用来将图形缓冲区buffer的内容渲染到帧缓冲区中去
int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
//用来通知fb设备,图形缓冲区的组合工作已经完成
int (*compositionComplete)(struct framebuffer_device_t* dev);
void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);
int (*enableScreen)(struct framebuffer_device_t* dev, int enable);
//保留
void* reserved_proc[6];
} framebuffer_device_t;
gralloc_module_t用于描述gralloc模块信息
typedef struct gralloc_module_t {
struct hw_module_t common;
//映射一块图形缓冲区到一个进程的地址空间去
int (*registerBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle);
//取消映射一块图形缓冲区到一个进程的地址空间去
int (*unregisterBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle);
//锁定一个指定的图形缓冲区
int (*lock)(struct gralloc_module_t const* module,buffer_handle_t handle, int usage,
int l, int t, int w, int h,void** vaddr);
//解锁一个指定的图形缓冲区
int (*unlock)(struct gralloc_module_t const* module,buffer_handle_t handle);
int (*perform)(struct gralloc_module_t const* module,int operation, ... );
void* reserved_proc[7];
} gralloc_module_t;
alloc_device_t用于描述gralloc设备的信息
typedef struct alloc_device_t {
struct hw_device_t common;
//用于分配一块图形缓冲区
int (*alloc)(struct alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* handle, int* stride);
//用于释放指定的图形缓冲区
int (*free)(struct alloc_device_t* dev,buffer_handle_t handle);
void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len);
void* reserved_proc[7];
} alloc_device_t;
typedef struct hw_module_t {
uint32_t tag;//标签
uint16_t version_major;//模块主设备号
uint16_t version_minor;//模块次设备号
const char *id;//模块ID
const char *name;//模块名称
const char *author;//模块作者
struct hw_module_methods_t* methods;//模块操作方法
void* dso;//保存模块首地址
uint32_t reserved[32-7];//保留位
} hw_module_t;
硬件抽象层Gralloc模块定义了设备fb和设备gpu:
#define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0"
设备gpu用于分配图形缓冲区,而设备fb用于渲染图形缓冲区;hw_module_t用于描述硬件抽象层Gralloc模块,而hw_device_t则用于描述硬件抽象层Gralloc设备,通过硬件抽象层设备可以找到对应的硬件抽象层模块。在Gralloc模块中,无论是定义fb设备还是gpu设备,都是用来处理图形缓冲区,以下是关于图形缓冲区的数据结构 定义:
private_handle_t用来描述一块图形缓冲区,Android对图形缓冲区的定义提供了C和C++两种方式,C语言编译器下的定义:
struct private_handle_t {
struct native_handle nativeHandle;
enum {
PRIV_FLAGS_FRAMEBUFFER = 0x00000001
};
int fd; //指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
int magic;
int flags;//用来描述一个图形缓冲区的标志,当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的。
int size;//用来描述一个图形缓冲区的大小
int offset;//用来描述一个图形缓冲区的偏移地址
int base;//用来描述一个图形缓冲区的实际地址
int pid;//用来描述一个图形缓冲区的创建者的PID
};
C++编译器下的定义:
struct private_handle_t : public native_handle {
enum {
PRIV_FLAGS_FRAMEBUFFER = 0x00000001
};
int fd; //指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
int magic;//指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体。
int flags;//用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER
int size;//用来描述一个图形缓冲区的大小。
int offset;//用来描述一个图形缓冲区的偏移地址。
int base;//用来描述一个图形缓冲区的实际地址,它是通过成员变量offset来计算得到的。
int pid;//用来描述一个图形缓冲区的创建者的PID。
static const int sNumInts = 6; //包含有6个整数
static const int sNumFds = 1; //包含有1个文件描述符
static const int sMagic = 0x3141592;
};
两种编译器下的private_handle_t定义都继承于native_handle,native_handle的定义如下:
typedef struct native_handle
{
int version; //设置为结构体native_handle_t的大小,用来标识结构体native_handle_t的版本
int numFds; //表示结构体native_handle_t所包含的文件描述符的个数,这些文件描述符保存在成员变量data所指向的一块缓冲区中。
int numInts; //表示结构体native_handle_t所包含的整数值的个数,这些整数保存在成员变量data所指向的一块缓冲区中。
int data[0]; //指向的一块缓冲区中
} native_handle_t;
typedef const native_handle_t* buffer_handle_t;
Gralloc模块的打开过程在 Android硬件抽象Hardware库加载过程源码分析 中详细分析过了,下面就分析Gralloc模块中定义了两种设备的打开过程。
Fb设备打开过程
fb设备的ID值定义为#defineGRALLOC_HARDWARE_FB0"fb0",fb设备使用结构体framebuffer_device_t来描述。结构体framebuffer_device_t是用来描述系统帧缓冲区的信息
hardware\libhardware\include\hardware\fb.h
static inline int framebuffer_open(const struct hw_module_t* module,
struct framebuffer_device_t** device) {
return module->methods->open(module,GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);
}
module指向的是一个用来描述Gralloc模块的hw_module_t结构体,前面提到,它的成员变量methods所指向的一个hw_module_methods_t结构体的成员函数open指向了Gralloc模块中的函数gralloc_device_open
hardware\libhardware\modules\gralloc\gralloc.cpp
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
...
} else {
status = fb_device_open(module, name, device);
}
return status;
}
gralloc_device_open函数即可以打开fb设备,也可以用于打开gpu设备,这里根据设备名来区分打开的设备,对应fb设备,则调用fb_device_open函数来完成设备打开操作。
hardware\libhardware\modules\gralloc\framebuffer.cpp
int fb_device_open(hw_module_t const* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
//判断打开的是fb设备
if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
alloc_device_t* gralloc_device;
//打开gpu设备
status = gralloc_open(module, &gralloc_device);
if (status < 0)
return status;
//创建一个fb_context_t对象,用来描述fb设备上下文
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
//初始化fb_context_t对象
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
//注册fb设备的操作函数
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0; private_module_t* m = (private_module_t*)module;
//将fb映射到当前进程地址空间
status = mapFrameBuffer(m);
if (status >= 0) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
int format = (m->info.bits_per_pixel == 32)
? HAL_PIXEL_FORMAT_RGBX_8888
: HAL_PIXEL_FORMAT_RGB_565;
const_cast<uint32_t&>(dev->device.flags) = 0;
const_cast<uint32_t&>(dev->device.width) = m->info.xres;
const_cast<uint32_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.stride) = stride;
const_cast<int&>(dev->device.format) = format;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;
const_cast<int&>(dev->device.minSwapInterval) = 1;
const_cast<int&>(dev->device.maxSwapInterval) = 1;
*device = &dev->device.common;
}
}
return status;
}
这个函数主要是用来创建一个fb_context_t结构体,并且对它的成员变量device进行初始化。结构体fb_context_t的成员变量device的类型为framebuffer_device_t,它是用来描述fb设备的。fb设备主要是用来渲染图形缓冲区的,这是通过调用它的成员函数post来实现的。函数fb_device_open所打开的fb设备的成员函数post被设置为Gralloc模块中的函数fb_post。函数mapFrameBuffer除了用来获得系统帧缓冲区的信息之外,还会将系统帧缓冲区映射到当前进程的地址空间来。line_length用来描述显示屏一行像素总共所占用的字节数,bits_per_pixel用来描述显示屏每一个像素所占用的位数,bits_per_pixel的值向右移3位,就可以得到显示屏每一个像素所占用的字节数。用显示屏像素总共所占用的字节数line_length除以每一个像素所占用的字节数就可以得到显示屏一行有多少个像素点,并保存在stride中。
static int mapFrameBuffer(struct private_module_t* module)
{
pthread_mutex_lock(&module->lock);
int err = mapFrameBufferLocked(module);
pthread_mutex_unlock(&module->lock);
return err;
}
调用mapFrameBufferLocked函数执行映射过程,该函数在线程保护下完成。
int mapFrameBufferLocked(struct private_module_t* module)
{
// already initialized...
if (module->framebuffer) {
return 0;
}
char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
0 }; int fd = -1;
int i=0;
char name[64];
//检查是否存在设备文件/dev/graphics/fb0或者/dev/fb0。如果存在的话,那么就调用函数open来打开它,并且将得到的文件描述符保存在变量fd中
while ((fd==-1) && device_template[i]) {
snprintf(name, 64, device_template[i], 0);
fd = open(name, O_RDWR, 0);
i++;
}
if (fd < 0)
return -errno;
//通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的固定信息,保存在fb_fix_screeninfo结构体finfo中
struct fb_fix_screeninfo finfo;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
//通过IO控制命令FBIOGET_VSCREENINFO来获得系统帧缓冲区的可变信息,保存在fb_var_screeninfo结构体info中
struct fb_var_screeninfo info;
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
//初始化info
info.reserved[0] = 0;
info.reserved[1] = 0;
info.reserved[2] = 0;
info.xoffset = 0;
info.yoffset = 0;
info.activate = FB_ACTIVATE_NOW;
//fb_var_screeninfo的成员变量xres和yres用来描述显示屏的可视分辨率,而成员变量xres_virtual和yres_virtual用来描述显示屏的虚拟分辨率。
//将虚拟分辨率的高度值设置为可视分辨率的高度值的NUM_BUFFERS倍。
info.yres_virtual = info.yres * NUM_BUFFERS; //2
uint32_t flags = PAGE_FLIP;
//设置设备显示屏的虚拟分辨率
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
//设置失败,重新设置显示屏的虚拟分辨率
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
}
if (info.yres_virtual < info.yres * 2) {
// we need at least 2 for page-flipping
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)",info.yres_virtual, info.yres*2);
}
//通过IO控制命令FBIOGET_VSCREENINFO来重新获得系统帧缓冲区的可变信息
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
//计算设备显示屏的刷新频率
uint64_t refreshQuotient =
(
uint64_t( info.upper_margin + info.lower_margin + info.yres )* ( info.left_margin + info.right_margin + info.xres )* info.pixclock
);
//模拟器的info.pixclock=0,因此计算得到的refreshQuotient=0
int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;
//如果是模拟器,设置刷新频率为60 Hz
if (refreshRate == 0) {
refreshRate = 60*1000; // 60 Hz
}
if (int(info.width) <= 0 || int(info.height) <= 0) {
info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
}
//计算显示屏的密度
float xdpi = (info.xres * 25.4f) / info.width;
float ydpi = (info.yres * 25.4f) / info.height;
float fps = refreshRate / 1000.0f;
//再次通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的固定信息
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
if (finfo.smem_len <= 0)
return -errno;
//得到的系统帧缓冲区的其它信息来初始化参数module所描述的一个private_module_t结构体。
module->flags = flags;
module->info = info;
module->finfo = finfo;
module->xdpi = xdpi;
module->ydpi = ydpi;
module->fps = fps;
int err;
//整个系统帧缓冲区的大小=虚拟分辨率的高度值info.yres_virtual * 每一行所占用的字节数finfo.line_length,并将整个系统帧缓冲区的大小对齐到页面边界
size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
//创建一个图形缓冲区描述体,该图形缓冲区的大小为整个系统帧缓冲区的大小
module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);
//计算整个系统帧缓冲区可以划分为多少个图形缓冲区来使用
module->numBuffers = info.yres_virtual / info.yres;
//表示系统帧缓冲区中的所有图形缓冲区都是处于空闲状态
module->bufferMask = 0;
//以读写共享方式将帧缓冲区映射到当前进程地址空间中
void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (vaddr == MAP_FAILED) {
ALOGE("Error mapping the framebuffer (%s)", strerror(errno));
return -errno;
}
//系统帧缓冲区在当前进程的地址空间中的起始地址保存到private_handle_t的域base中
module->framebuffer->base = intptr_t(vaddr);
//清空大小为fbSize的帧缓冲区
memset(vaddr, 0, fbSize);
return 0;
}
Gpu设备打开过程
gralloc设备使用结构体alloc_device_t来描述。结构体alloc_device_t有两个成员函数alloc和free,分别用来分配和释放图形缓冲区,gralloc设备的ID值定义为:
#defineGRALLOC_HARDWARE_GPU0"gpu0"
hardware\libhardware\include\hardware\gralloc.h
static inline int gralloc_open(const struct hw_module_t* module,
struct alloc_device_t** device) {
return module->methods->open(module,
GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);
}
module指向的是一个用来描述Gralloc模块的hw_module_t结构体,它的成员变量methods所指向的一个hw_module_methods_t结构体的成员函数open指向了Gralloc模块中的函数gralloc_device_open。前面介绍了函数gralloc_device_open即可以打开fb设备也可用来打开gpu设备,这里传入的设备名为GRALLOC_HARDWARE_GPU0,表示当前打开的是gpu设备。
hardware\libhardware\modules\gralloc\gralloc.cpp
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free = gralloc_free;
*device = &dev->device.common;
status = 0;
} else {
...
}
return status;
}
这个函数主要是用来创建一个gralloc_context_t结构体,并且对它的成员变量device进行初始化。结构体gralloc_context_t的成员变量device的类型为gralloc_device_t,它用来描述一个gralloc设备。前面提到,gralloc设备是用来分配和释放图形缓冲区的,这是通过调用它的成员函数alloc和free来实现的。从这里可以看出,函数gralloc_device_open所打开的gralloc设备的成员函数alloc和free分别被设置为Gralloc模块中的函数gralloc_alloc和gralloc_free。
FrameBuffer驱动程序分析 文中介绍了Linux系统下的显示驱动框架,每个显示屏被抽象为一个帧缓冲区,注册到FrameBuffer模块中,并在/dev/graphics目录下创建对应的fbX设备。Android系统在硬件抽象层中提供了一个Gralloc模块,封装了对帧缓冲区的所有访问操作。用户空间的应用程序在使用帧缓冲区之间,首先要加载Gralloc模块,并且获得一个gralloc设备和一个fb设备。有了gralloc设备之后,用户空间中的应用程序就可以申请分配一块图形缓冲区,并且将这块图形缓冲区映射到应用程序的地址空间来,以便可以向里面写入要绘制的画面的内容。最后,用户空间中的应用程序就通过fb设备来将已经准备好了的图形缓冲区渲染到帧缓冲区中去,即将图形缓冲区的内容绘制到显示屏中去。相应地,当用户空间中的应用程序不再需要使用一块图形缓冲区的时候,就可以通过gralloc设备来释放它,并且将它从地址空间中解除映射。
Gralloc模块实现源码位于:hardware/libhardware/modules/gralloc
.
├── Android.mk
├── framebuffer.cpp
├── gralloc.cpp
├── gralloc_priv.h
├── gr.h
└── mapper.cpp
Android硬件抽象Hardware库加载过程源码分析 介绍了Android系统中的硬件抽象层模块的加载过程,并指出每个硬件抽象层模块都必须定义HAL_MODULE_INFO_SYM符号,并且有自己唯一的ID,Gralloc也不例外,Gralloc模块ID定义为:
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
同时定义了以HAL_MODULE_INFO_SYM为符号的类型为private_module_t的结构体:
hardware\libhardware\modules\gralloc\gralloc.cpp
static struct hw_module_methods_t gralloc_module_methods = {
open: gralloc_device_open
};
struct private_module_t HAL_MODULE_INFO_SYM = {
base: {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: GRALLOC_HARDWARE_MODULE_ID,
name: "Graphics Memory Allocator Module",
author: "The Android Open Source Project",
methods: &gralloc_module_methods
},
registerBuffer: gralloc_register_buffer,
unregisterBuffer: gralloc_unregister_buffer,
lock: gralloc_lock,
unlock: gralloc_unlock,
},
framebuffer: 0,
flags: 0,
numBuffers: 0,
bufferMask: 0,
lock: PTHREAD_MUTEX_INITIALIZER,
currentBuffer: 0,
};
通过 Android硬件抽象Hardware库加载过程源码分析 的方法将Gralloc模块加载到内存中来之后,就可以调用函数dlsym来获得它所导出的符号HMI,得到private_module_t的首地址后,由于private_module_t的第一个成员变量的类型为gralloc_module_t,因此也是gralloc_module_t的首地址,由于gralloc_module_t的第一个成员变量类型为hw_module_t,因此也是hw_module_t的首地址,因此只要得到这三种类型中其中一种类型变量的地址,就可以相互转换为其他两种类型的指针。
数据结构定义
在分析Gralloc模块之前,首先介绍Gralloc模块定义的一些数据结构。private_module_t用于描述Gralloc模块下的系统帧缓冲区信息
struct private_module_t {
gralloc_module_t base;
private_handle_t* framebuffer; //指向系统帧缓冲区的句柄
uint32_t flags; //用来标志系统帧缓冲区是否支持双缓冲
uint32_t numBuffers;//表示系统帧缓冲区包含有多少个图形缓冲区
uint32_t bufferMask; //记录系统帧缓冲区中的图形缓冲区的使用情况
pthread_mutex_t lock; //一个互斥锁,用来保护结构体private_module_t的并行访问
buffer_handle_t currentBuffer; //用来描述当前正在被渲染的图形缓冲区
int pmem_master;
void* pmem_master_base;
struct fb_var_screeninfo info; //保存设备显示屏的动态属性信息
struct fb_fix_screeninfo finfo; ////保存设备显示屏的固定属性信息
float xdpi; //描述设备显示屏在宽度
float ydpi; //描述设备显示屏在高度
float fps; //用来描述显示屏的刷新频率
};
framebuffer_device_t用来描述系统帧缓冲区设备的信息
typedef struct framebuffer_device_t {
struct hw_device_t common;
const uint32_t flags;//用来记录系统帧缓冲区的标志
const uint32_t width;//用来描述设备显示屏的宽度
const uint32_t height;//用来描述设备显示屏的高度
const int stride;//用来描述设备显示屏的一行有多少个像素点
const int format;//用来描述系统帧缓冲区的像素格式
const float xdpi;//用来描述设备显示屏在宽度上的密度
const float ydpi;//用来描述设备显示屏在高度上的密度
const float fps;//用来描述设备显示屏的刷新频率
const int minSwapInterval;//用来描述帧缓冲区交换前后两个图形缓冲区的最小时间间隔
const int maxSwapInterval;//用来描述帧缓冲区交换前后两个图形缓冲区的最大时间间隔
int reserved[8];//保留
//用来设置帧缓冲区交换前后两个图形缓冲区的最小和最大时间间隔
int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);
//用来设置帧缓冲区的更新区域
int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);
//用来将图形缓冲区buffer的内容渲染到帧缓冲区中去
int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
//用来通知fb设备,图形缓冲区的组合工作已经完成
int (*compositionComplete)(struct framebuffer_device_t* dev);
void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);
int (*enableScreen)(struct framebuffer_device_t* dev, int enable);
//保留
void* reserved_proc[6];
} framebuffer_device_t;
gralloc_module_t用于描述gralloc模块信息
typedef struct gralloc_module_t {
struct hw_module_t common;
//映射一块图形缓冲区到一个进程的地址空间去
int (*registerBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle);
//取消映射一块图形缓冲区到一个进程的地址空间去
int (*unregisterBuffer)(struct gralloc_module_t const* module,buffer_handle_t handle);
//锁定一个指定的图形缓冲区
int (*lock)(struct gralloc_module_t const* module,buffer_handle_t handle, int usage,
int l, int t, int w, int h,void** vaddr);
//解锁一个指定的图形缓冲区
int (*unlock)(struct gralloc_module_t const* module,buffer_handle_t handle);
int (*perform)(struct gralloc_module_t const* module,int operation, ... );
void* reserved_proc[7];
} gralloc_module_t;
alloc_device_t用于描述gralloc设备的信息
typedef struct alloc_device_t {
struct hw_device_t common;
//用于分配一块图形缓冲区
int (*alloc)(struct alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* handle, int* stride);
//用于释放指定的图形缓冲区
int (*free)(struct alloc_device_t* dev,buffer_handle_t handle);
void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len);
void* reserved_proc[7];
} alloc_device_t;
typedef struct hw_module_t {
uint32_t tag;//标签
uint16_t version_major;//模块主设备号
uint16_t version_minor;//模块次设备号
const char *id;//模块ID
const char *name;//模块名称
const char *author;//模块作者
struct hw_module_methods_t* methods;//模块操作方法
void* dso;//保存模块首地址
uint32_t reserved[32-7];//保留位
} hw_module_t;
硬件抽象层Gralloc模块定义了设备fb和设备gpu:
#define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0"
设备gpu用于分配图形缓冲区,而设备fb用于渲染图形缓冲区;hw_module_t用于描述硬件抽象层Gralloc模块,而hw_device_t则用于描述硬件抽象层Gralloc设备,通过硬件抽象层设备可以找到对应的硬件抽象层模块。在Gralloc模块中,无论是定义fb设备还是gpu设备,都是用来处理图形缓冲区,以下是关于图形缓冲区的数据结构 定义:
private_handle_t用来描述一块图形缓冲区,Android对图形缓冲区的定义提供了C和C++两种方式,C语言编译器下的定义:
struct private_handle_t {
struct native_handle nativeHandle;
enum {
PRIV_FLAGS_FRAMEBUFFER = 0x00000001
};
int fd; //指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
int magic;
int flags;//用来描述一个图形缓冲区的标志,当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的。
int size;//用来描述一个图形缓冲区的大小
int offset;//用来描述一个图形缓冲区的偏移地址
int base;//用来描述一个图形缓冲区的实际地址
int pid;//用来描述一个图形缓冲区的创建者的PID
};
C++编译器下的定义:
struct private_handle_t : public native_handle {
enum {
PRIV_FLAGS_FRAMEBUFFER = 0x00000001
};
int fd; //指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
int magic;//指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体。
int flags;//用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER
int size;//用来描述一个图形缓冲区的大小。
int offset;//用来描述一个图形缓冲区的偏移地址。
int base;//用来描述一个图形缓冲区的实际地址,它是通过成员变量offset来计算得到的。
int pid;//用来描述一个图形缓冲区的创建者的PID。
static const int sNumInts = 6; //包含有6个整数
static const int sNumFds = 1; //包含有1个文件描述符
static const int sMagic = 0x3141592;
};
两种编译器下的private_handle_t定义都继承于native_handle,native_handle的定义如下:
typedef struct native_handle
{
int version; //设置为结构体native_handle_t的大小,用来标识结构体native_handle_t的版本
int numFds; //表示结构体native_handle_t所包含的文件描述符的个数,这些文件描述符保存在成员变量data所指向的一块缓冲区中。
int numInts; //表示结构体native_handle_t所包含的整数值的个数,这些整数保存在成员变量data所指向的一块缓冲区中。
int data[0]; //指向的一块缓冲区中
} native_handle_t;
typedef const native_handle_t* buffer_handle_t;
Gralloc模块的打开过程在 Android硬件抽象Hardware库加载过程源码分析 中详细分析过了,下面就分析Gralloc模块中定义了两种设备的打开过程。
Fb设备打开过程
fb设备的ID值定义为#defineGRALLOC_HARDWARE_FB0"fb0",fb设备使用结构体framebuffer_device_t来描述。结构体framebuffer_device_t是用来描述系统帧缓冲区的信息
hardware\libhardware\include\hardware\fb.h
static inline int framebuffer_open(const struct hw_module_t* module,
struct framebuffer_device_t** device) {
return module->methods->open(module,GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);
}
module指向的是一个用来描述Gralloc模块的hw_module_t结构体,前面提到,它的成员变量methods所指向的一个hw_module_methods_t结构体的成员函数open指向了Gralloc模块中的函数gralloc_device_open
hardware\libhardware\modules\gralloc\gralloc.cpp
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
...
} else {
status = fb_device_open(module, name, device);
}
return status;
}
gralloc_device_open函数即可以打开fb设备,也可以用于打开gpu设备,这里根据设备名来区分打开的设备,对应fb设备,则调用fb_device_open函数来完成设备打开操作。
hardware\libhardware\modules\gralloc\framebuffer.cpp
int fb_device_open(hw_module_t const* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
//判断打开的是fb设备
if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
alloc_device_t* gralloc_device;
//打开gpu设备
status = gralloc_open(module, &gralloc_device);
if (status < 0)
return status;
//创建一个fb_context_t对象,用来描述fb设备上下文
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
//初始化fb_context_t对象
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
//注册fb设备的操作函数
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0; private_module_t* m = (private_module_t*)module;
//将fb映射到当前进程地址空间
status = mapFrameBuffer(m);
if (status >= 0) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
int format = (m->info.bits_per_pixel == 32)
? HAL_PIXEL_FORMAT_RGBX_8888
: HAL_PIXEL_FORMAT_RGB_565;
const_cast<uint32_t&>(dev->device.flags) = 0;
const_cast<uint32_t&>(dev->device.width) = m->info.xres;
const_cast<uint32_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.stride) = stride;
const_cast<int&>(dev->device.format) = format;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;
const_cast<int&>(dev->device.minSwapInterval) = 1;
const_cast<int&>(dev->device.maxSwapInterval) = 1;
*device = &dev->device.common;
}
}
return status;
}
这个函数主要是用来创建一个fb_context_t结构体,并且对它的成员变量device进行初始化。结构体fb_context_t的成员变量device的类型为framebuffer_device_t,它是用来描述fb设备的。fb设备主要是用来渲染图形缓冲区的,这是通过调用它的成员函数post来实现的。函数fb_device_open所打开的fb设备的成员函数post被设置为Gralloc模块中的函数fb_post。函数mapFrameBuffer除了用来获得系统帧缓冲区的信息之外,还会将系统帧缓冲区映射到当前进程的地址空间来。line_length用来描述显示屏一行像素总共所占用的字节数,bits_per_pixel用来描述显示屏每一个像素所占用的位数,bits_per_pixel的值向右移3位,就可以得到显示屏每一个像素所占用的字节数。用显示屏像素总共所占用的字节数line_length除以每一个像素所占用的字节数就可以得到显示屏一行有多少个像素点,并保存在stride中。
static int mapFrameBuffer(struct private_module_t* module)
{
pthread_mutex_lock(&module->lock);
int err = mapFrameBufferLocked(module);
pthread_mutex_unlock(&module->lock);
return err;
}
调用mapFrameBufferLocked函数执行映射过程,该函数在线程保护下完成。
int mapFrameBufferLocked(struct private_module_t* module)
{
// already initialized...
if (module->framebuffer) {
return 0;
}
char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
0 }; int fd = -1;
int i=0;
char name[64];
//检查是否存在设备文件/dev/graphics/fb0或者/dev/fb0。如果存在的话,那么就调用函数open来打开它,并且将得到的文件描述符保存在变量fd中
while ((fd==-1) && device_template[i]) {
snprintf(name, 64, device_template[i], 0);
fd = open(name, O_RDWR, 0);
i++;
}
if (fd < 0)
return -errno;
//通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的固定信息,保存在fb_fix_screeninfo结构体finfo中
struct fb_fix_screeninfo finfo;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
//通过IO控制命令FBIOGET_VSCREENINFO来获得系统帧缓冲区的可变信息,保存在fb_var_screeninfo结构体info中
struct fb_var_screeninfo info;
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
//初始化info
info.reserved[0] = 0;
info.reserved[1] = 0;
info.reserved[2] = 0;
info.xoffset = 0;
info.yoffset = 0;
info.activate = FB_ACTIVATE_NOW;
//fb_var_screeninfo的成员变量xres和yres用来描述显示屏的可视分辨率,而成员变量xres_virtual和yres_virtual用来描述显示屏的虚拟分辨率。
//将虚拟分辨率的高度值设置为可视分辨率的高度值的NUM_BUFFERS倍。
info.yres_virtual = info.yres * NUM_BUFFERS; //2
uint32_t flags = PAGE_FLIP;
//设置设备显示屏的虚拟分辨率
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
//设置失败,重新设置显示屏的虚拟分辨率
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
}
if (info.yres_virtual < info.yres * 2) {
// we need at least 2 for page-flipping
info.yres_virtual = info.yres;
flags &= ~PAGE_FLIP;
ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)",info.yres_virtual, info.yres*2);
}
//通过IO控制命令FBIOGET_VSCREENINFO来重新获得系统帧缓冲区的可变信息
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
//计算设备显示屏的刷新频率
uint64_t refreshQuotient =
(
uint64_t( info.upper_margin + info.lower_margin + info.yres )* ( info.left_margin + info.right_margin + info.xres )* info.pixclock
);
//模拟器的info.pixclock=0,因此计算得到的refreshQuotient=0
int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;
//如果是模拟器,设置刷新频率为60 Hz
if (refreshRate == 0) {
refreshRate = 60*1000; // 60 Hz
}
if (int(info.width) <= 0 || int(info.height) <= 0) {
info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
}
//计算显示屏的密度
float xdpi = (info.xres * 25.4f) / info.width;
float ydpi = (info.yres * 25.4f) / info.height;
float fps = refreshRate / 1000.0f;
//再次通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的固定信息
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
if (finfo.smem_len <= 0)
return -errno;
//得到的系统帧缓冲区的其它信息来初始化参数module所描述的一个private_module_t结构体。
module->flags = flags;
module->info = info;
module->finfo = finfo;
module->xdpi = xdpi;
module->ydpi = ydpi;
module->fps = fps;
int err;
//整个系统帧缓冲区的大小=虚拟分辨率的高度值info.yres_virtual * 每一行所占用的字节数finfo.line_length,并将整个系统帧缓冲区的大小对齐到页面边界
size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
//创建一个图形缓冲区描述体,该图形缓冲区的大小为整个系统帧缓冲区的大小
module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);
//计算整个系统帧缓冲区可以划分为多少个图形缓冲区来使用
module->numBuffers = info.yres_virtual / info.yres;
//表示系统帧缓冲区中的所有图形缓冲区都是处于空闲状态
module->bufferMask = 0;
//以读写共享方式将帧缓冲区映射到当前进程地址空间中
void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (vaddr == MAP_FAILED) {
ALOGE("Error mapping the framebuffer (%s)", strerror(errno));
return -errno;
}
//系统帧缓冲区在当前进程的地址空间中的起始地址保存到private_handle_t的域base中
module->framebuffer->base = intptr_t(vaddr);
//清空大小为fbSize的帧缓冲区
memset(vaddr, 0, fbSize);
return 0;
}
Gpu设备打开过程
gralloc设备使用结构体alloc_device_t来描述。结构体alloc_device_t有两个成员函数alloc和free,分别用来分配和释放图形缓冲区,gralloc设备的ID值定义为:
#defineGRALLOC_HARDWARE_GPU0"gpu0"
hardware\libhardware\include\hardware\gralloc.h
static inline int gralloc_open(const struct hw_module_t* module,
struct alloc_device_t** device) {
return module->methods->open(module,
GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);
}
module指向的是一个用来描述Gralloc模块的hw_module_t结构体,它的成员变量methods所指向的一个hw_module_methods_t结构体的成员函数open指向了Gralloc模块中的函数gralloc_device_open。前面介绍了函数gralloc_device_open即可以打开fb设备也可用来打开gpu设备,这里传入的设备名为GRALLOC_HARDWARE_GPU0,表示当前打开的是gpu设备。
hardware\libhardware\modules\gralloc\gralloc.cpp
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free = gralloc_free;
*device = &dev->device.common;
status = 0;
} else {
...
}
return status;
}
这个函数主要是用来创建一个gralloc_context_t结构体,并且对它的成员变量device进行初始化。结构体gralloc_context_t的成员变量device的类型为gralloc_device_t,它用来描述一个gralloc设备。前面提到,gralloc设备是用来分配和释放图形缓冲区的,这是通过调用它的成员函数alloc和free来实现的。从这里可以看出,函数gralloc_device_open所打开的gralloc设备的成员函数alloc和free分别被设置为Gralloc模块中的函数gralloc_alloc和gralloc_free。
Android图形显示之硬件抽象层Gralloc(hal 转)的更多相关文章
- Android图形显示之硬件抽象层Gralloc【转】
https://blog.csdn.net/yangwen123/article/details/12192401 FrameBuffer驱动程序分析文中介绍了Linux系统下的显示驱动框架,每个显示 ...
- Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析[转]
前面在介绍Android系统的开机画面时提到,Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户 ...
- Android 12(S) 图形显示系统 - 解读Gralloc架构及GraphicBuffer创建/传递/释放(十四)
必读: Android 12(S) 图形显示系统 - 开篇 一.前言 在前面的文章中,已经出现过 GraphicBuffer 的身影,GraphicBuffer 是Android图形显示系统中的一个重 ...
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口(老罗学习笔记4)
在上两篇文章中,我们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象层接口.实现这两者的目的是为了向更上一层提供硬件访问接口,即为 ...
- Android - 硬件抽象层(HAL)
以下资料摘录整理自老罗的Android之旅博客,是对老罗的博客关于Android底层原理的一个抽象的知识概括总结(如有错误欢迎指出)(侵删):http://blog.csdn.net/luosheng ...
- 为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
在上两篇文章中,我们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象层接 口.实现这两者的目的是为了向更上一层提供硬件访问接口,即 ...
- 为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
在Android硬件抽象层(HAL)概要介绍和学习计划一文中,我们简要介绍了在Android系统为为硬件编写驱动程序的方法.简单来说,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的 ...
- Android源码分析二 硬件抽象层(HAL)
一 什么是HAL HAL 可定义一个标准接口以供硬件供应商实现,这可让 Android 忽略较低级别的驱动程序实现.借助 HAL,您可以顺利实现相关功能,而不会影响或更改更高级别的系统.HAL 实现会 ...
- 第九章 硬件抽象层:HAL
这一章介绍HAL,全称为Hardware Abstract Layer,即硬件抽象层,它是建立在Linux驱动之上的一套程序库,程序库并不属于Linux内核,而是属于Linux内核层之上的应用层.为A ...
随机推荐
- R cannot be resolved to a variable问题
在调试android的时候,layout解析都正确,但是build project的时候提示" R cannot be resolved to a variable " 经过查找, ...
- c# 财务数据编号的生辰
实现逻辑: 根据票号前戳+生成的6位数据编号=收据号(010+000001=010000001) 作废票号 将票号作废,插入到编号表中,以此下次在使用 作废票号使用 编号表中 现在只有2,当 ...
- 关于Java中的GUI事件处理
关于事件监听的实现过程通过下面的代码来具体说明: package com.sxt; import java.awt.BorderLayout; import java.awt.event.Action ...
- Uniform Generator 分类: HDU 2015-06-19 23:26 11人阅读 评论(0) 收藏
Uniform Generator Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) T ...
- Opencv中直线的表示方法
[blog算法原理]Opencv中直线的表示方法 一.问题的提出: 在实际项目编写过程中,需要对直线(Line)进行特定的处 ...
- LInux ugo权限详解[修]
Linux 中的用户和组是用来控制使用者或者进程可以或者不可以使用哪些资源和硬件,是Linux权限控制最基本的方式. 用户和组可以看一下上一章的部分,先来看一下权限. 一.权限概览 在Linux下,使 ...
- 几篇不错的基础css博客转载
CSS 巧用 :before和:after:http://web.jobbole.com/85083/ css清除元素间距:http://ouvens.github.io/frontend-css/2 ...
- Mysql-学习笔记(==》连接查询_高级查询五)
CREATE TABLE t( tid INT UNSIGNED NOT NULL AUTO_INCREMENT, tname VARCHAR(30), PRIMARY KEY (tid))ENGIN ...
- FlexCell控件的使用
private void grid1_GetCellText(object Sender, FlexCell.Grid.GetCellTextEventArgs e) { // 要使用虚表,可以在Gr ...
- linux下查看分区信息和剩余空间大小
1. 查看Linux系统分区信息,使用命令“fdisk -l” 2.使用命令”df -l和df -h“具体查看分区使用状况.实际这两个命令具有一样的作用区别是显示的容量单位不一样,当然也可以直接使用明 ...