刚才怎么发到it168的blog去了,cu的跳转有我呢? it168不能超过一万字,发不了,分了两篇

还是觉得这边好,发这边一篇。

kdrive的xvide的加速的实现。

首先xvideo本来也是在驱动里面实现的,实际上我们先要做一个驱动。

kdrive硬件加速驱动的实现,其实就是kaa的实现,kaa分xserver这边和driver这边,xserver这边会提供一种机制

这个机制就是当gc操作的时候会判断一下回调函数是否注册,如果注册就使用回调函数,如果没有注册就使用软件的实现,

而注册这个回调函数就是在kdrive的驱动里面来实现的。因为kdrive不能动态装载驱动,所以驱动都是直接编译到Xserver

里面的,也就是说编译出来的Xfbdev是直接带了驱动的。

我们先看看代码的布局,之前是在xserver 1.5.x的基础上来实现的,大概就以1.5.3来讲吧

关于kdrive和xserver的关系之前说过,kdrive是xserver的一种,xserver在xorg的代码树里面有好几种实现,但是大体的代码都是共用的

比如基本的事件处理,clip等等代码都是共用的。hw目录下面就是不同的xserver的实现。xorg只是其中一种,如前所说,kdrive和xorg有很多

代码是共用的,所以xorg的代码更新过快,kdrive更新过慢,导致kdrive的有很多事件处理有问题。

本来应该先说kaa的实现的,但是因为xivdeo的实现比较简单,所以先说。

我们先看看xvideo的工作流程,就是说xvideo到底是怎么发挥作用的。

xvideo的目的是为了把video或者frame输出直接输出到屏幕上面,尽量少的转存,尽量多的利用硬件加速,而xorg是管理最后输出的(目前是这样)

所以需要在xserver里面有一个扩展这个就是Xv的扩展了,

这里需要一个图片说明

我们看到最原始的数据无非是一个视频文件,我们先假定这个文件是一个mpeg4的文件。然后通过demux之后就有不同的数据流,比如音频,视频,还有其他的,

但是我们这里先只关注视频,视频流出来之后就经过解码,解码完成之后就成了一帧一帧的图片,和放电影的原理是一样的。然后我们就要把这个图片显示

到屏幕上面,最终的输出函数一般都是XPutImage,XvPutImage,XvShmPutImage类似这样的函数。

当然使用shm的版本会比不使用shm的版本要快很多了,Xshm本来也是Xserver的一个扩展,这个主要是为了减少数据的copy

一般来说写的好的播放器都会判断一些xserver是否支持shm扩展,如果支持,就会使用shm的函数。比如mplayer,gstreamer(xvimagesink)

因为Xserver这个框架是C/S模式的,说这个很好理解,但是很多人还是知其然不知其所以然,我们从原始的架构来理解。

原始的X是可以通过网络来传输的,但是因为这种模式我们现在用的不是很多了,除了ssh -X abc@192.168.1.100这样的用法之外,可能我们

很少会用到这样的远程传输,我们本机一般用的是unix socket。也就是说所有的请求,实际上是这样的,

我们编写应用程序里面会有XPutImage的函数,实际上这个函数是由libX11来提供的(如果我没有记错的话),XvSHmPutImage这样的函数是由libXext来实现的。

这个里面的代码实际上就是封装一个X的请求,然后通过Xsend发送出去。然后Xserver那边会dispatch这些消息,由对应的扩展或者函数来处理,因为这些Xext初始化

的时候会告诉Xserver这类消息由自己的扩展来处理。

所以想想如果是一个很大帧的数据,都走socket的话是很慢的,所以有了Xshm就好很多了,shm,和我们普通的shm是一样的,就是共享内存了,就是用户程序和

xserver共享一个内存空间,当用户程序填充帧的时候实际上就直接写到xserver的内存空间了(需要图片说明),然后xserver可以直接显示出去,这样会节省很多空间

Ximage这个数据接口是有data指针的,但是如果是shm的版本就没有,里面会带一个地址。XshmImage.这些内容都是凭记忆写的,可能会有漏误

话说回来,Xvideo在xserver这边对应的处理流程。

xserver无非也会接收到XshmPutimage的请求,这个在Xserver里面xorg-server-1.5.3/Xext/shm.c里面实现的

static int

SProcShmPutImage(client)

ClientPtr client;

{

register int n;

REQUEST(xShmPutImageReq);

swaps(&stuff->length, n);

REQUEST_SIZE_MATCH(xShmPutImageReq);

swapl(&stuff->drawable, n);

swapl(&stuff->gc, n);

swaps(&stuff->totalWidth, n);

swaps(&stuff->totalHeight, n);

swaps(&stuff->srcX, n);

swaps(&stuff->srcY, n);

swaps(&stuff->srcWidth, n);

swaps(&stuff->srcHeight, n);

swaps(&stuff->dstX, n);

swaps(&stuff->dstY, n);

swapl(&stuff->shmseg, n);

swapl(&stuff->offset, n);

return ProcShmPutImage(client);

}

ProcShmPutImage这个函数会调用下面的函数下面调用的是XPutImage的注册回调函数了,

(*pGC->ops->PutImage) (pDraw, pGC, stuff->depth,

stuff->dstX, stuff->dstY,

stuff->totalWidth, stuff->srcHeight,

stuff->srcX, stuff->format,

shmdesc->addr + stuff->offset +

(stuff->srcY * length));

从dispatch开始看

Xext/xvdisp.c

#define XVCALL(name) Xv##name

ProcXvPutImage

最后调用的是这个,

return XVCALL(diPutImage)(client, pDraw, pPort, pGC,

stuff->src_x, stuff->src_y,

stuff->src_w, stuff->src_h,

stuff->drw_x, stuff->drw_y,

stuff->drw_w, stuff->drw_h,

pImage, (unsigned char*)(&stuff[1]), FALSE,

stuff->width, stuff->height);

实际上是下面的函数XvdiPutImage

Xext/xvmain.c这个函数是xvideo的具体实现,当然xorg和kdrive实际上都用这里的代码。

XvdiPutImage

调用下面的回调函数了

return (* pPort->pAdaptor->ddPutImage)(client, pDraw, pPort, pGC,

src_x, src_y, src_w, src_h,

drw_x, drw_y, drw_w, drw_h,

image, data, sync, width, height);

我们要实现的自然就是ddPutImage了。

这个函数就是gc注册的了,这里我们使用kdrive的框架,自然就是kaa去注册的了。

然后kaa的具体实现,也就是我们的驱动,会注册kaa的函数。

所有kdrive的函数都在

xorg-server-1.5.3/hw/kdrive

ailantian@vax:/mnt/sdb1/ubd/soft/xorg/temp/xorg-server-1.5.3/hw/kdrive$ ls

ati    ephyr  fake   i810   mach64       Makefile.in  neomagic  pm2   sdl     smi  vesa

chips  epson  fbdev  linux  Makefile.am  mga          nvidia    r128  sis300  src  via

ailantian@vax:/mnt/sdb1/ubd/soft/xorg/temp/xorg-server-1.5.3/hw/kdrive$ pwd

这下面基本每个目录都是一个driver,比如有intel 810, ati,等等,很多驱动了。

kaa这个框架是在hw/kdrive/kaa.c里面实现的,至于xvideo的框架的实现就是在kxv里面来实现的了。

我们的驱动里面通过一定的机制实际上会调用到下面这个函数,

KdXVInitAdaptors

pa->pScreen = pScreen;

pa->ddAllocatePort = KdXVAllocatePort;

pa->ddFreePort = KdXVFreePort;

pa->ddPutVideo = KdXVPutVideo;

pa->ddPutStill = KdXVPutStill;

pa->ddGetVideo = KdXVGetVideo;

pa->ddGetStill = KdXVGetStill;

pa->ddStopVideo = KdXVStopVideo;

pa->ddPutImage = KdXVPutImage;

pa->ddSetPortAttribute = KdXVSetPortAttribute;

pa->ddGetPortAttribute = KdXVGetPortAttribute;

pa->ddQueryBestSize = KdXVQueryBestSize;

pa->ddQueryImageAttributes = KdXVQueryImageAttributes;

实际上我们只关注

pa->ddPutImage = KdXVPutImage;

这个函数是在xserver的xvideo扩展里面被调用的。

也就是说kxv实际上是一个框架,然后会有人来填充KdXVPutImage这样的回调函数的,这个就是我们kaa驱动里面要做的事情了。

其实i810等等这些驱动可以直接参考,我们可以直接copy一下一个目录,然后作为自己驱动开发的基础,然后改改makefile就行了。

这里还是以ati作为例子来讲解吧,我自己的代码写的比较乱,可能还有其他一些问题,所以不便贴出来。

一般来说xvideo的实现都是在xxx_video.c里面实现的,比如我们看ati的,就是在ati_video.c里面了。

其实代码量比较小。

其实输出出去,最终还是这个函数,

ATIPutImage,然后这个函数一般会调用DisplayVideo这样的函数,不过这个只是开发者的习惯而已。

那么先看看框架。

以ati为例,注册回调函数从哪里开始,

grep一下代码就知道了,实际上是这个。

ATISetupImageVideo

ATIInitVideo调用ATISetupImageVideo

看个人的喜好了,反正是要注册这些回调函数了,

下面是关键代码,所谓的框架都在这里了。

static KdVideoAdaptorPtr

ATISetupImageVideo(ScreenPtr pScreen)

{

KdScreenPriv(pScreen);

ATIScreenInfo(pScreenPriv);

KdVideoAdaptorPtr adapt;

ATIPortPrivPtr pPortPriv;

int i;

atis->num_texture_ports = 16;

adapt = xcalloc(1, sizeof(KdVideoAdaptorRec) + atis->num_texture_ports *

(sizeof(ATIPortPrivRec) + sizeof(DevUnion)));

if (adapt == NULL)

return NULL;

adapt->type = XvWindowMask | XvInputMask | XvImageMask;

adapt->flags = VIDEO_CLIP_TO_VIEWPORT;

adapt->name = "ATI Texture Video";

adapt->nEncodings = 1;

adapt->pEncodings = DummyEncoding;

adapt->nFormats = NUM_FORMATS;

adapt->pFormats = Formats;

adapt->nPorts = atis->num_texture_ports;

adapt->pPortPrivates = (DevUnion*)(&adapt[1]);

pPortPriv =

(ATIPortPrivPtr)(&adapt->pPortPrivates[atis->num_texture_ports]);

for (i = 0; i < atis->num_texture_ports; i++)

adapt->pPortPrivates[i].ptr = &pPortPriv[i];

adapt->nAttributes = NUM_ATTRIBUTES;

adapt->pAttributes = Attributes;

adapt->pImages = Images;

adapt->nImages = NUM_IMAGES;

adapt->PutVideo = NULL;

adapt->PutStill = NULL;

adapt->GetVideo = NULL;

adapt->GetStill = NULL;

adapt->StopVideo = ATIStopVideo;

adapt->SetPortAttribute = ATISetPortAttribute;

adapt->GetPortAttribute = ATIGetPortAttribute;

adapt->QueryBestSize = ATIQueryBestSize;

adapt->PutImage = ATIPutImage;

adapt->ReputImage = ATIReputImage;

adapt->QueryImageAttributes = ATIQueryImageAttributes;

/* gotta uninit this someplace */

REGION_INIT(pScreen, &pPortPriv->clip, NullBox, 0);

atis->pAdaptor = adapt;

xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");

xvSaturation = MAKE_ATOM("XV_SATURATION");

return adapt;

}

看看上面最重要的函数就是注册了    adapt->PutImage = ATIPutImage;

这个函数在最后就会被调用到。

大家可能会问这个adapt怎么会和上面的KdXVPutImage打上关系,这个自然是要注册的。

KdXVScreenInit->KdXVInitAdaptors

adaptorPriv->flags = adaptorPtr->flags;

adaptorPriv->PutVideo = adaptorPtr->PutVideo;

adaptorPriv->PutStill = adaptorPtr->PutStill;

adaptorPriv->GetVideo = adaptorPtr->GetVideo;

adaptorPriv->GetStill = adaptorPtr->GetStill;

adaptorPriv->StopVideo = adaptorPtr->StopVideo;

adaptorPriv->SetPortAttribute = adaptorPtr->SetPortAttribute;

adaptorPriv->GetPortAttribute = adaptorPtr->GetPortAttribute;

adaptorPriv->QueryBestSize = adaptorPtr->QueryBestSize;

adaptorPriv->QueryImageAttributes = adaptorPtr->QueryImageAttributes;

adaptorPriv->PutImage = adaptorPtr->PutImage;

adaptorPriv->ReputImage = adaptorPtr->ReputImage;

portPriv->AdaptorRec = adaptorPriv;

这个函数指针都放在了AdaptorRec这个里面,而这个是在KdXVXXXX函数里面被调用的。

比如

KdXVPutImage

到输出的时候就会尝试调用这个注册的回调函数了。

ret = (*portPriv->AdaptorRec->PutImage)(portPriv->screen, pDraw,

src_x, src_y, WinBox.x1, WinBox.y1,

src_w, src_h, drw_w, drw_h, format->id, data, width, height,

sync, &ClipRegion, portPriv->DevPriv.ptr);

而这个回调函数就是我们之前ATISetupImageVideo里面注册的函数,通过一系列周转到这里来的。

回到正题,只看最重要的部分,如何硬件加速输出的。其实看代码grep一下,肯定是和寄存器相关,或者mmio相关的,因为老的框架都是使用mmio的。

ATIPutImage里面加入了Xrandr的处理,这个我们可以先不管,主要是屏幕旋转会造成问题,所以需要处理一下,不过我们如果不旋转可以不关注这个代码了。

最关键的代码如下我们解码出来的图片是在内存里面,但是实际上我们要显示出去,必须放到显存里面,这里我不想讲kaa的内存管理了,偏离标题了,

if (pPortPriv->off_screen == NULL) {

pPortPriv->off_screen = KdOffscreenAlloc(screen->pScreen,

size * 2, 64, TRUE, ATIVideoSave, pPortPriv);

if (pPortPriv->off_screen == NULL)

return BadAlloc;

}

反正就是先我们要在显存里面划拨一个区域来存放这个图片才行。

下面有一段pixmap的判断,这个先不说,

总之,我们必须保证这个东西是在显存里面,无论是不是offscreen的。

然后就是下面的代码了,

switch(id) {

case FOURCC_YV12:

case FOURCC_I420:

top &= ~1;

nlines = ((((rot_y2 + 0xffff) >> 16) + 1) & ~1) - top;

KdXVCopyPlanarData(screen, buf, pPortPriv->src_addr, randr,

srcPitch, srcPitch2, dstPitch, rot_src_w, rot_src_h,

height, top, left, nlines, npixels, id);

break;

case FOURCC_UYVY:

case FOURCC_YUY2:

default:

nlines = ((rot_y2 + 0xffff) >> 16) - top;

KdXVCopyPackedData(screen, buf, pPortPriv->src_addr, randr,

srcPitch, dstPitch, rot_src_w, rot_src_h, top, left,

nlines, npixels);

break;

}

这个代码实际上是用来进行数据格式的转换的,也就是说,其实显卡所支持的硬件加速的格式是有限的,如果这个格式不被硬件所支持的话,我们就需要进行软件的

格式转换,这里xserver处理的有点死板,xserver到最后的输出的时候只支持两种格式,所以所有的格式都会转换成那两种格式,如果没有记错的话

应该是YVYU,VYUY,也就是说xserver目前的框架,是将数据封装成这两个格式,然后给硬件的,当然,这个驱动是自己实现的,一般来说,硬件支持很多种格式,

还有我们看到上面的代码,从user application传递过来的之后四种格式,这个是因为我们的驱动只注册了四种格式,也就是说我们只支持这四种格式,

类似mplayer会通过函数来查询当前的驱动到底支持哪几种格式,xvinfo这个x自带的工具也可以看。

注册格式是在这个地方,如果硬件支持更多的比如NV12,NV21等可以加在这里,好像一般的嵌入式的处理器如果有2d处理的话,都会支持。

static KdImageRec Images[NUM_IMAGES] =

{

XVIMAGE_YUY2,

XVIMAGE_YV12,

XVIMAGE_I420,

XVIMAGE_UYVY

};

xserver默认只支持这几个fourcc,如果要更多,可以从别的驱动里面抄这些定义,或者抄mplayer或者pixman里面都有这些fourcc的定义。因为很多格式还没有被

标准化。

数据pack完成之后自然就是发送给硬件显示了。

这个就比较简单了,因为我们有一个缓冲区,这个缓冲区已经在显存里面了,我们要显示到屏幕的某个地方。

注意offscreen和online screen的区别,比如我们有一个显卡,显存500M,实际上只有很小一部分空间是对应在屏幕上面的。

一般来说,显卡操作显存,是比较快的,有各种不同的技术实现,各家也不一样。但是比内存是要快的,或者至少也要和内存的速度差不多,大块数据的搬运会比较快一些。

比如要从offscreen搬运到online screen,其实嵌入式来说,显存也是内存,划拨一块而已,但是这个操作一般不需要cpu参与了。

好,我们看看显示的过程,

一般驱动程序会单独写一个函数来处理这个过程。

这里其实只有两个需要注意的地方,一个就是clip的处理,一个就是composite的处理。

虽然说xvideo本身的设计是和composite背离的,不过xvideo也可以直接输出到一个texure或者是一个pixmap里面。因为无论是什么方式,无非是一块显存。

R128DisplayVideo

我们看看这个实现。

BoxPtr pBox = REGION_RECTS(&pPortPriv->clip);

int nBox = REGION_NUM_RECTS(&pPortPriv->clip);

这里是计算clip的,比如我们播放一个视频的时候,这个时候有菜单盖住屏幕,这样我们的输出区域就被剪切成多个区域了,2个或者说个等等。

我们就只能按小块输出了,每块单独输出,但是还是硬件加速的,我们看看循环就知道了。

while (nBox--) {

int srcX, srcY, dstX, dstY, srcw, srch, dstw, dsth;

dstX = pBox->x1 + dstxoff;

dstY = pBox->y1 + dstyoff;

dstw = pBox->x2 - pBox->x1;

dsth = pBox->y2 - pBox->y1;

srcX = (pBox->x1 - pPortPriv->dst_x1) *

pPortPriv->src_w / pPortPriv->dst_w;

srcY = (pBox->y1 - pPortPriv->dst_y1) *

pPortPriv->src_h / pPortPriv->dst_h;

srcw = pPortPriv->src_w - srcX;

srch = pPortPriv->src_h - srcY;

BEGIN_DMA(6);

OUT_RING(DMA_PACKET0(R128_REG_SCALE_SRC_HEIGHT_WIDTH, 2));

OUT_RING_REG(R128_REG_SCALE_SRC_HEIGHT_WIDTH,

(srch << 16) | srcw);

OUT_RING_REG(R128_REG_SCALE_OFFSET_0, pPortPriv->src_offset +

srcY * pPortPriv->src_pitch + srcX * 2);

OUT_RING(DMA_PACKET0(R128_REG_SCALE_DST_X_Y, 2));

OUT_RING_REG(R128_REG_SCALE_DST_X_Y, (dstX << 16) | dstY);

OUT_RING_REG(R128_REG_SCALE_DST_HEIGHT_WIDTH,

(dsth << 16) | dstw);

END_DMA();

pBox++;

}

里面的OUT_RING我们就不看了,简单点理解,就是硬件输出。

各家硬件不一样,这里的目的是一样的,就是输出区域。nBox就是我们区域的个数了。

我们要把buffer输出到一个指定的区域,指定的偏移就可以了。

至于composite,我们无非是要指定输出的区域就行了,这个区域如果不在online screen,我们就输出到pixmap了。

然后composite会管理输出,可能是windowmanager做这个或者是xcompmgr类似这样的程序。

DamageDamageRegion(pPortPriv->pDraw, &pPortPriv->clip);

这个是需要的。xv的框架输出,我们需要汇报一下,我们写了这个区域,如果有人也在使用这块区域,就需要知道这个事情。

所以实际上xv的框架涉及到的扩展比较多

Xv,Xcomposite,Xdamage,Xfixes,

大体上就是这样了,其他的函数都是一些辅助的函数,直接照抄就行了。

另外要说明的一点就是。各种不同的flag的作用。

VIDEO_CLIP_TO_VIEWPORT等等,这个是根据不同的显示模式来设置的。

overlay和texture是不一样的,overlay在现在好像用的很少了,不过因为是硬件的支持,可能比texture会快一些,毕竟一个单独的layer操作

起来加上硬件的alpha blending会快的多,而texture的输出,完全靠显卡,感觉现在的显卡虽然很强了,但是面对大量的数据的alpha blending

无论是从显存大小,还是数据传输还是计算能力,都还是不够的。pc如此,嵌入式更如此。

想想现在都是大分辨率1440x900这个是一般的小屏幕了,24bit depth,然后还要加上composite,需求加倍。内存占用就很多了,还有就是所有的窗口都要先

offscreen render一下,然后再说alpha blending,很慢,我也不知道composite有什么好,为了酷炫,就要付出更多的代价。

唉,用的是fcitx的全拼打字,还是有点慢的,上面的东西写了一个半小时,其他事情也还比较多

kaa不知道什么时候写了。

kdriver+kaa+kxv

xorg+exa+xv

两个框架其实是类似的,xorg扩展性更强一些,可以动态装载。

xorg的input 自动探测也比较简单,还像说说。evdev统一天下还需时日。

晕,输入居然多余的万字了,我太能了:)

看来我写小说还挺快,一个半小时能超过一万字呢

漏掉一点就是为什么要使用xvideo

这个之前的文章说过,就是我们从mpeg4或者h264等等文件解码出来的实际上是yuv的数据,比如可能是yuv422的

但是我们的屏幕实际上是rgb的,不同的系统不一样了,比如我们的pc一般rgb888的,对于嵌入式来说可能会选择rgb565这样的,所以就需要转换,

这个yuv转rgb是需要很多计算量的,另外就是硬件缩放,比如解码出来的图像是vga或者wvga的,我们要全屏显示的时候就需要缩放,这个时候也非常占用cpu。

没记错的话,应该都是浮点运算。虽然coretex-a8等等都有了vfp等等指令,但是实际上,这点性能还是远远不够。还有就是xvideo的设计就是为了快速的输出,

所以从框架上来说是很精简的,为了快速显示而设计的。

不过有一点要注意就是profile,我们一般会profile一下,看看这个xvideo的实现性能在什么地方,因为现在输出都是由硬件来做的,所以cpu占用很少,

后来用oprofile看的时候发现其实cpu占用最多的地方居然在数据封装的地方,也就是我们把用户程序传递过来的数据封装成硬件支持的格式。

这个地方还是可以用汇编优化一下的。改善很明显。毕竟是驱动里面最"热"的地方。

不过后来因为迁移到了xorg,所以kdrive这边的实现,包括kaa的驱动和xvideo的实现都没再继续用了

xorg这边有芯片提供商在开发xorg的dri+exa的driver,所以我就没再做这个工作了。

阅读(2453) | 评论(1) | 转发(0) |

linux远程连接硬件加速,xorg 硬件加速浅析 系列 kdrive的xvide的加速的实现相关推荐

  1. linux远程连接硬件加速,linux – 没有X的硬件加速

    答案取决于您的用户应用程序.如果一切都是裸机,而您的应用团队正在编写所有内容,那么 DirectFB API可以用作Fredrik的建议.如果您使用GTK的帧缓冲版本,这可能会特别有趣. 但是,如果您 ...

  2. linux如何关闭硬件加速,启用硬件加速是什么意思?如何关闭【详解】

    导语:小编相信,经常会使用到电脑的朋友们,对于启用硬件加速这个词一定都是不陌生的吧!可是呢,对于一些电脑小白们来说,往往会搞不清楚,这个启用硬件加速到底是个什么意思呢?启用之后,我们的电脑又会发生什么 ...

  3. Linux 6.2 最新合并情况:拓展 ArmSoc 支持,华为代码加速核心功能 715 倍!

    整理 | 王启隆 作为送给全球开发者的圣诞礼物,Linux 在前日发布了 Linux 6.1 内核的稳定版,并开启了 Linux 6.2 的合并窗口.这次更新不仅为广大用户带来了不少新功能与改进,还让 ...

  4. 安卓禁用硬件加速_Android硬件加速

    最近项目中遇到了因为硬件加速引起的一些问题,故这里深入学习了解一下关于硬件加速的一些东西 背景 什么是硬件加速? 硬件加速是Android系统在绘制图形时采取的一种方式. 图形的绘制,本质上就是界面的 ...

  5. 怎么关闭计算机硬件加速,启用硬件加速是什么意思?如何关闭[详细说明]

    引言:我相信经常使用计算机的朋友必须熟悉"硬件加速"一词!但是,对于某些计算机新手,他们常常感到困惑.启用硬件加速是什么意思?启用它之后,我们的计算机会发生什么?有些人不知道在启用 ...

  6. 安卓禁用硬件加速_硬件加速  |  Android 开发者  |  Android Developers

    从 Android 3.0(API 级别 11)开始,Android 2D 渲染管道支持硬件加速,也就是说,在 如果您的目标 API 级别为 14 及更高级别,则硬件加速默认处于启用状态,但也可以明确 ...

  7. 浅析门户网站体育赛事CDN加速解决方案

    经过近些年来的高速发展,互联网已经成为现今媒体传播的主要力量.这一点在大型体育赛事的报道与转播上,体现的尤为明显.更及时的信息内容.更多样的报道形式.更齐全的资料组成.更方便的阅读查询.更具互动性的报 ...

  8. linux启动xorg进程,Linux 黑话解释:Xorg,X11,Wayland,什么是显示服务器

    原标题:Linux 黑话解释:Xorg,X11,Wayland,什么是显示服务器 您是否想知道X Server,Xorg,X11,Wayland以及诸如此类的东西到底是什么?Wayland vs Xo ...

  9. 【模型推理加速系列】06: 基于resnet18加速方案评测

    简介 花雪随风不厌看,更多还肯失林峦.愁人正在书窗下,一片飞来一片寒.小伙伴们好,我是微信公众号小窗幽记机器学习的首席称重师:卖麻辣烫的小男孩.今天这篇文章以resnet18模型为例,对比Pytorc ...

最新文章

  1. Android通过ksoap2调用.net(c#)的webservice
  2. 课时 18-Kubernetes 调度和资源管理(子誉)
  3. java dotnet core_在.NET Core 2.0中使用Omron库
  4. 整理下STL algorithms(3)
  5. 【C语言】在线OJ题 BC7-BC52-牛客网编程初学者入门训练
  6. 细粒度图像分类_【完结】16篇图像分类干货文章总结,从理论到实践全流程大盘点!...
  7. Chrome插件(扩展)开发全攻略
  8. 手机图形计算器matlab,图形计算器Mathlab
  9. 使用毫秒单位来进行计算程序执行时间
  10. Defender绝密档案:惊现中本聪?
  11. JVM(java堆)
  12. MPLab X 配置字的设置
  13. 安装软件出现nsis error对话框的解决方法
  14. jQuery添加元素的方法
  15. 一拖二组装设备,一台信捷XD5-60T10-E系列PLC控制两台组装检测设备,扩展5个32点输入输出扩展模块
  16. 【Unity3d】 教会你如何做一个简单的电梯系统(升降平台)
  17. 三菱FX3UPLC增量型PID(完整梯形图源代码)
  18. python发票打印程序_用python打印正确的票据生成程序
  19. harmony应用主题样式
  20. C语言实现直角坐标转换为极坐标

热门文章

  1. Android 实现背景音乐的播放及震动
  2. 光波 声波 区别
  3. SSH学习之Session接口的常用方法
  4. [09]项目实战-PC 端固定布局(9)
  5. Boost型开关电源应用之TPS54340
  6. MySQL这几本书,初学者必看!
  7. 【C/C++】 学习笔记
  8. [心情]其实我只想要一份稳定有发展潜力的工作,为公司做出最大的贡献之余也丰富我的人生阅历的工作而已从而达到我人生的巅峰...
  9. Vue3中使用各类字体图标的正确姿势:本地SVG、Iconfont、FontAwesome、ElementPlus(Icon篇)
  10. 【Redis】Redis过期删除策略和内存淘汰策略剖析(Redis专栏启动)