当前位置: 首页 > news >正文

押注网站建设如何用dw做网站底页

押注网站建设,如何用dw做网站底页,如何建设网站吸引人,区块链开发与应用Pag的渲染 背景 根据Pag文章里面说的#xff0c;Pag之前长时间使用的Skia库作为底层渲染引擎。但由于Skia库体积过大#xff0c;为了保证通用型#xff08;比如兼容CPU渲染#xff09;做了很多额外的事情。所以Pag的工程师们自己实现了一套2D图形框架替换掉Skia#xff…Pag的渲染 背景 根据Pag文章里面说的Pag之前长时间使用的Skia库作为底层渲染引擎。但由于Skia库体积过大为了保证通用型比如兼容CPU渲染做了很多额外的事情。所以Pag的工程师们自己实现了一套2D图形框架替换掉Skia在github里面在一个叫 TGFX 的目录内。我猜测腾讯的工程师这里是致敬暴雪的bgfx吧不过这两个库其实有本质区别bgfx更多是统一底层图形API为目标的而tgfx是一套用于2D的渲染库核心目标是替换掉Skia。其目前主要用OpenGL API用GPU实现渲染默认不支持CPU渲染也能通过SwiftShader来支持在绝大部分设备上GPU渲染都会更高效。其API也和Skia的API相似度很高只是没有SK开头罢了。这些API对于做Android应用开发的同学来说再熟悉不过了。不过我还是整体理一下里面的核心概念吧 概念描述Bitmap位图它是像素的集合是内存里的色彩的表现和承载者一般用CPU解码图片后就会保存为Bitmap也可以通过CPU去操作Bitmap的像素。Texture纹理也代表一张图在OpenGL内使用可以简单的理解为在显存内的BitmapPag里面的视频/素材 都是通过Texture渲染的。Surface表面其实更像是一个装画的地方。画画我们可以在纸上画也可以在墙上画。Paint画笔可以自定义各种色彩样式等拿起什么样的比就画出什么样的画。Canvas画布可以看做一种渲染过程比如draw/drawTexture/drawPath等。经历过怎样的渲染过程就会产出什么样的结果。比如我画了一个狮子再画一个老虎那最终就会呈现出虎狮 概要 为了了解全貌我这边把一些重要的概念和整体的Pag模块画了一个图大家可以看到TGFX在整个libpag内的位置 接下来我们还是从情景来分析pag渲染的具体流程。 Pag渲染流程分析 之前我们介绍了Pag的基本结构组织这里来详细讲一下渲染过程也就是如下两个方法调用 1. 设置进度 pagPlayer.setProgress(progress); 2. 执行渲染 pagPlayer.flush();设置进度 这个应该都大致能猜到肯定核心逻辑就是会去设置到目标合成里通过归一化的percent通知到RootComposition当前有进度更新需要准备下一次渲染的素材了之类的逻辑。具体代码如下 void PAGPlayer::setProgress(double percent) {LockGuard autoLock(rootLocker);auto pagComposition stage-getRootComposition();if (pagComposition nullptr) {return;}auto realProgress percent;auto frameRate pagComposition-frameRateInternal();if (_maxFrameRate frameRate _maxFrameRate 0) {auto duration pagComposition-durationInternal();auto totalFrames TimeToFrame(duration, frameRate);auto numFrames ceilf(totalFrames * _maxFrameRate / frameRate);// 首先计算在maxFrameRate的帧号之后重新计算progressauto targetFrame ProgressToFrame(realProgress, numFrames);realProgress FrameToProgress(targetFrame, numFrames);}pagComposition-setProgressInternal(realProgress); }发现这里其实还有一个播放器的最大渲染帧率的转换逻辑如果合成的帧率比PagPlayer的目标帧率还高需要根据间隔去做采样为播放器的最大帧率。这个在低端设备上应该会对性能会有一些提升能够更大程度的利用缓存。核心的pagComposition-setProgressInternal(realProgress);是实现在PagLayer内的它会通过progress计算出当前应该播放的时间然后调用gotoTimeAndNotifyChanged方法 bool PAGLayer::gotoTimeAndNotifyChanged(int64_t targetTime) {auto changed gotoTime(targetTime);if (changed) {notifyModified();}return changed; }顾名思义先Seek到指定的时间然后如果发现图层需要更新核心是一个_stretchedFrameDuration这个是在AE插件里面保存的值如果是静态图之类的会和duration相同更新progress不需要改变内容则会调用notifyModified去通知有更新我们看看里面的实现 void PAGLayer::notifyModified(bool contentChanged) {if (contentChanged) {contentVersion;}auto parentLayer getParentOrOwner();while (parentLayer) {parentLayer-contentVersion;parentLayer parentLayer-getParentOrOwner();} }可以看到首先把自己的contentVersion做了自增然后如果有父图层以及父图层更新contentVersion我们这里本身是根图层更新不会涉及parent。下一次渲染的时候会对比这个contentVersion和上一次的是否一致如果一致的话就不需要重新绘制了通过这个version来避免额外性能损耗。 渲染过程 渲染过程的流程主要是分为 组织渲染 – 主要是通过调用Canvas的一些方法把所有渲染相关的指令组织起来保存到上下文内执行渲染 – 把上面组织起来的所有渲染相关的指令flush到GPU内完成一次渲染上屏渲染 – 等待渲染操作完成把渲染完成的Buffer上屏到显示设备上 我这里先画一个简单的类图把里面涉及到的大致的概念呈现出来让大家有一个简单的认识。把里面的一些主要概念的关系画出来也包含了里面的一些功能说明。 组织渲染 Pag执行渲染的方法叫做flush就是把当前设置的这些配置要给执行了。Flush的实现核心是调用了flushInternal方法 bool PAGPlayer::flushInternal(BackendSemaphore* signalSemaphore) {...prepareInternal();clock.mark(rendering);if (!pagSurface-draw(renderCache, lastGraphic, signalSemaphore, _autoClear)) {return false;}...return true; }我们这里省略了大部分其他代码直接看了两个最重要的prepareInternal和pagSurface-draw()调用就可以了。 prepareInternal的实现如下 void PAGPlayer::prepareInternal() {// 为了提升性能预加载加载视频和图片renderCache-prepareLayers();// 通过contentVersion来判断stage是否有刷新没有刷新的话就不用去重新渲染了if (contentVersion ! stage-getContentVersion()) {// 更新当前的渲染内容版本号contentVersion stage-getContentVersion();Recorder recorder {};// 难道在这里就渲染了并不是哦只是组织图层到recorder里面stage-draw(recorder);lastGraphic recorder.makeGraphic();}if (lastGraphic) {lastGraphic-prepare(renderCache);} }可以看到这里在判断需要刷新stage的时候有个stage-draw(recorder);调用stage代码如下 void PAGComposition::draw(Recorder* recorder) {... // 这里省略缓存策略auto preComposeLayer static_castPreComposeLayer*(layer);auto composition preComposeLayer-composition;... // 这里省略位图或者视频策略Clip和判空逻辑auto count static_castint(layers.size());// 遍历所有的图层然后挨个调用DrawChildLayerfor (int i 0; i count; i) {auto childLayer layers[i];if (!childLayer-layerVisible) {continue;}DrawChildLayer(recorder, childLayer.get());}... // 省略Clip逻辑 }这个方法核心其实就是遍历所有的childLayer然后挨个调用DrawChildLayer把每一层存入Recorder内我们看看DrawChildLayer的代码 void PAGComposition::DrawChildLayer(Recorder* recorder, PAGLayer* childLayer) {// 图层的特效Modifierauto filterModifier childLayer-cacheFilters() ? nullptr : FilterModifier::Make(childLayer);// 多点追踪使用的暂时可以不用理会auto trackMatte TrackMatteRenderer::Make(childLayer);Transform extraTransform {ToTGFX(childLayer-layerMatrix), childLayer-layerAlpha};LayerRenderer::DrawLayer(recorder, childLayer-layer,childLayer-contentFrame childLayer-layer-startTime, filterModifier,trackMatte.get(), childLayer, extraTransform); }看到这个代码有点晕了开始有TGFX的影子了。这里核心是调用静态函数LayerRenderer::DrawLayer void LayerRenderer::DrawLayer(Recorder* recorder, Layer* layer, Frame layerFrame,std::shared_ptrFilterModifier filterModifier,TrackMatte* trackMatte, Content* layerContent,Transform* extraTransform) {if (TransformIllegal(extraTransform) || TrackMatteIsEmpty(trackMatte)) {return;}auto contentFrame layerFrame - layer-startTime;// 这里比较核心返回一个layoutCache里面有包含各种图层类型的渲染内容缓存的实现。auto layerCache LayerCache::Get(layer);if (!layerCache-contentVisible(contentFrame)) {return;}auto content layerContent ? layerContent : layerCache-getContent(contentFrame);... //省略alphaBlend, 多点追踪逻辑auto saveCount recorder-getSaveCount();... // 省略其他TransfermMask等逻辑// 核心draw逻辑content-draw(recorder);recorder-restoreToCount(saveCount);... // 省略多点追踪逻辑recorder-restore(); }可以看到这里核心流程就是通过 auto layerCache LayerCache::Get(layer); 创建/获取了一个LayerCache。然后执行LayerCache的content的draw方法到recorder对象内。看看LayerCache::Get的代码 LayerCache* LayerCache::Get(Layer* layer) {std::lock_guardstd::mutex autoLock(layer-locker);if (layer-cache nullptr) {layer-cache new LayerCache(layer);}return static_castLayerCache*(layer-cache); } LayerCache::LayerCache(Layer* layer) : layer(layer) {switch (layer-type()) {case LayerType::Shape:contentCache new ShapeContentCache(static_castShapeLayer*(layer));break;case LayerType::Text:contentCache new TextContentCache(static_castTextLayer*(layer));break;case LayerType::Solid:contentCache new SolidContentCache(static_castSolidLayer*(layer));break;case LayerType::Image:contentCache new ImageContentCache(static_castImageLayer*(layer));break;case LayerType::PreCompose:contentCache new PreComposeContentCache(static_castPreComposeLayer*(layer));break;default:contentCache new EmptyContentCache(layer);break;}contentCache-update();transformCache new TransformCache(layer);if (!layer-masks.empty()) {maskCache new MaskCache(layer);}updateStaticTimeRanges();maxScaleFactor ToTGFX(layer-getMaxScaleFactor()); }其实核心就是通过ContentCache类型分别创建GraphicContent类型。这个GraphicContent是一个用于具体实现不同类型的图形的结构了。有一点像Android里面的View。接下来调用了核心的content.draw(recorder)把需要渲染的GraphicsContent存入到我们的Recorder内。 void Recorder::drawGraphic(std::shared_ptrGraphic graphic) {auto content Graphic::MakeCompose(std::move(graphic), matrix);if (content nullptr) {return;}if (layerIndex 0) {rootContents.push_back(content);} else {layerContents.push_back(content);} }从这里也说明了这些函数虽然叫做drawXXX但是在这一步并没有真正的执行任何渲染相关的指令。 接下来回到一开始的prepareInternal我们已经分析了这里通过这个Recorder保存了渲染需要用的content。prepareInternal最后做的事情就是通过这个recorder再生成一个具体用于渲染的Graphic对象并且再prepare这个Graphic lastGraphic recorder.makeGraphic();if (lastGraphic) {lastGraphic-prepare(renderCache);}这个lastGraphic是一个LayerGraphic而下一步lastGraphic-prepare才真正的是prepare要渲染的内容的。LayerGraphic是一个树形结构的包装对象和Android里面的ViewGroup十分类似。其prepare也就是深度遍历这个渲染树把所有的节点都prepare一遍。 void LayerGraphic::prepare(RenderCache* cache) const {for (auto content : contents) {content-prepare(cache);} }针对不同的Content会有不同的prepare过程。比如对图片的内容来说就可以在这里去获取解码好的图片如果没有解码好需要等待解码完成视频也需要在这里等这个时间的帧解码好才能去做渲染。 渲染核心过程 组织渲染指令 准备工作看完了我们这里开始来深入分析一下这个pagSurface-draw。先上代码这里我加上了注释 bool PAGSurface::draw(RenderCache* cache, std::shared_ptrGraphic graphic,BackendSemaphore* signalSemaphore, bool autoClear) {// 之前提到过这里的drawable代表的是一个抽象的GPU渲染实例GPUDrawable/OffscreenDrawable等并不是做具体绘制的实例。if (!drawable-prepareDevice()) {return false;}// 获取用于渲染的上下文这里拿到的是GLContextauto context lockContext();if (!context) {return false;}if (surface ! nullptr autoClear contentVersion cache-getContentVersion()) {unlockContext();return false;}// 如果Surface为空则创建。这里的Surface是指的TGFX内的if (surface nullptr) {surface drawable-createSurface(context);}if (surface nullptr) {unlockContext();return false;}contentVersion cache-getContentVersion();cache-attachToContext(context);auto canvas surface-getCanvas();if (autoClear) {canvas-clear();}if (graphic) {// 核心渲染的操作会生成draw callgraphic-draw(canvas, cache);}// 这里具体去执行draw call把CPU内组合的渲染操作(Op最终同步到GPU内if (signalSemaphore nullptr) {// 同步模式下直接flush操作surface-flush();} else {// 这里多了异步渲染模式下的同步锁操作。tgfx::GLSemaphore semaphore {};surface-flush(semaphore);signalSemaphore-initGL(semaphore.glSync);}cache-detachFromContext();drawable-setTimeStamp(pagPlayer-getTimeStampInternal());drawable-present(context);unlockContext();return true; }上面最核心的是两个调用。一个是生成DrawCall的 if (graphic) {graphic-draw(canvas, cache);}最终会把在prepareInternal里面生成的所有Content调用draw来生成这个content当前状态对应的OpenGL渲染指令集。我们这里用绘制一个Shape来举例 void Shape::draw(tgfx::Canvas* canvas, RenderCache* renderCache) const {tgfx::Paint paint;auto snapshot renderCache-getSnapshot(this);if (snapshot) {...// 这里省略了使用缓存的逻辑 ...}paint.setShader(shader);canvas-drawPath(path, paint); }是不是和我们在Android里面的drawPath很像这里的canvas的实现是GLCanvas void GLCanvas::fillPath(const Path path, const Paint paint) {...// 注意这里创建了一个GLDrawOpauto op MakeSimplePathOp(path, glPaint, state-matrix);if (op) {draw(std::move(op), std::move(glPaint));return;}...op GLTriangulatingPathOp::Make(glPaint.color, tempPath, state-clip.getBounds(), localMatrix);if (op) {save();resetMatrix();draw(std::move(op), std::move(glPaint));restore();return;}...drawMask(deviceBounds, mask-makeTexture(getContext()), std::move(glPaint)); }我们这里省略了很多代码基本可以看到流程就是创建GLDrawOp在里面设置一堆参数最后调用draw或者drawMask最终也是调用到了draw我们来看看这个draw方法 void GLCanvas::draw(std::unique_ptrGLDrawOp op, GLPaint paint, bool aa) {if (drawContext nullptr) {return;}// 设置抗锯齿类型auto aaType AAType::None;if (static_castGLSurface*(surface)-renderTarget-sampleCount() 1) {aaType AAType::MSAA;} else if (aa !IsPixelAligned(op-bounds())) {aaType AAType::Coverage;} else {const auto matrix state-matrix;auto rotation std::round(RadiansToDegrees(atan2f(matrix.getSkewX(), matrix.getScaleX())));if (static_castint(rotation) % 90 ! 0) {aaType AAType::Coverage;}}// 设置Maskauto masks std::move(paint.coverageFragmentProcessors);Rect scissorRect Rect::MakeEmpty();auto clipMask getClipMask(op-bounds(), scissorRect);if (clipMask) {masks.push_back(std::move(clipMask));}// 设置渲染区域op-setScissorRect(scissorRect);// 设置Blend图层叠加模式unsigned first;unsigned second;if (BlendAsCoeff(state-blendMode, first, second)) {op-setBlendFactors(std::make_pair(first, second));} else {op-setXferProcessor(PorterDuffXferProcessor::Make(state-blendMode));op-setRequireDstTexture(!GLCaps::Get(getContext())-frameBufferFetchSupport);}op-setAA(aaType);// 配置颜色op-setColors(std::move(paint.colorFragmentProcessors));op-setMasks(std::move(masks));// 添加到渲染队列drawContext-addOp(std::move(op)); } 基本的核心配置我已经在代码里给了注释。这个地方特别重要属于渲染的核心流程所以代码我没有省略。 这里设置了一些Paint的通用配置然后加入到绘制的上下文drawContext-addOp(std::move(op)); void SurfaceDrawContext::addOp(std::unique_ptrOp op) {getOpsTask()-addOp(std::move(op)); }很简单其实就是把当前这一个Content的Op添加到opsTask列表里面了。说明这里其实还没有具体去执行这些OpenGL指令。值得注意的是这个getOpsTask里面有一个很重要的操作 OpsTask* SurfaceDrawContext::getOpsTask() {if (opsTask nullptr || opsTask-isClosed()) {replaceOpsTask();}return opsTask.get(); }void SurfaceDrawContext::replaceOpsTask() {opsTask surface-getContext()-drawingManager()-newOpsTask(surface); }这个opsTask是通过replaceOpsTask里面的surface-getContext()-drawingManager()-newOpsTask(surface);创建的。这样就会被DrawingManager所管理起来之后再被统一执行。 执行渲染 上面基本就把准备渲染的opTask准备好了。其实渲染的准备工作是最重要的执行渲染只是把前面存放的即将用于渲染的指令具体去执行罢了。 if (signalSemaphore nullptr) {surface-flush();} else {tgfx::GLSemaphore semaphore {};surface-flush(semaphore);signalSemaphore-initGL(semaphore.glSync);}很明显这里面的这个Surface-flush就是渲染具体执行的地方了。里面的代码很简单 bool Surface::flush(Semaphore* signalSemaphore) {// 解析渲染的目标。因为Pag支持设置渲染到纹理上不一定都是上屏所以这里会手动插入一个配置Target的TaskrenderTarget-getContext()-drawingManager()-newTextureResolveRenderTask(this);// 这里把之前保存的opTask都执行了return renderTarget-getContext()-drawingManager()-flush(signalSemaphore); }我们看看这里核心调用的DrawingManager-flush具体是做哪些事 bool DrawingManager::flush(Semaphore* signalSemaphore) {auto* gpu context-gpu();closeAllTasks();activeOpsTask nullptr;for (auto task : tasks) {task-execute(gpu);}removeAllTasks();return context-caps()-semaphoreSupport gpu-insertSemaphore(signalSemaphore); }可以看到这里的核心就是会把之前所有的GLCanvas执行的opTask都执行一遍然后再清空这个tasks列表。这样就完成了渲染指令的具体执行了。 上屏渲染 在PagSurface的draw最后几段代码如下 drawable-setTimeStamp(pagPlayer-getTimeStampInternal());drawable-present(context);一开始的时候提到过Pag里面的Drawable其实是一个渲染设备的封装。这个present在最后会调用到OpenGL的swapbuffer函数将前面的所有操作交换缓冲区实现上屏到具体的渲染Target。这里我们就不具体去分析了。 总结 今天这个文章里面的代码比较多了有一点乱而且这里只是一次渲染还没有讲到Pag如何管理渲染的缓存实现上一期讲的纵向分层水平分块把性能提升的。如果要深入理解建议自己去Debug到Pag的流程里面才能真正的了解内部渲染的执行过程能够学到更多的东西。
http://www.dnsts.com.cn/news/280147.html

相关文章:

  • 用asp做网站的可行性分析电商网站定制
  • 什邡市建设局网站网帆网站建设
  • 门户网站策划方案html5响应式网站模板
  • 谁家做网站游戏推广员怎么做
  • 手机开发网站工具怎么用云服务器做网站
  • 湖北网站开发培训深圳网站设计制作元
  • 网盘爬虫源码是做网站来用的义乌多语言网站建设
  • 石家庄开发网站域名在哪里申请
  • 手机网站推广服务国内有类似wordpress
  • 邯郸网站建设优化排名yw最新域名备案查询
  • 已经收录大规模修改收录页面对网站有影响吗莱芜融媒体中心网站
  • 佛山网站优化方法软件wordpress下载链接框
  • 怎么确定电商网站建设的目标做头像网站
  • 网站建立不安全怎么取消网上贸易平台有哪些
  • 微网站模板建设的选择网页制作交城旅游景点
  • 罗湖网站的建设网站建设发生的成本如何记账
  • 广陵区建设局网站今天的国内新闻
  • 怎么建自己的手机网站做网络写手赚钱的网站
  • 商务网站的建设与维护百度关键词搜索量查询
  • 洛阳电商网站建设楼盘网站模板
  • 网站没有内容 备案能成功吗苏州网络推广seo服务
  • 注册一个公司网站需要多少钱微信小程序网站建设公司
  • 网站建设东莞数字重庆公司
  • 网站建设提供的网站资料网站怎么做域名
  • 网站建设优化推广哈尔滨网站点击量查询
  • 小的电商网站电子网站建设推广方案
  • 青海城乡建设厅网站 官网ios wordpress 编辑器
  • 网站建设是如何称呼的市场营销推广策划
  • 网站开发设计实训实训总结淮安app开发公司
  • 建站宝盒自助建站系统手机在线做ppt模板下载网站