写点什么

PAG 动效框架源码笔记 (四)渲染框架

作者:olinone
  • 2023-05-31
    上海
  • 本文字数:4094 字

    阅读完需:约 13 分钟

转载请注明出处:http://www.olinone.com/

前言

PAG 采用自研 TGFX 特效渲染引擎,抽象分离了接口及平台实现类,可以扩展支持多种图形渲染库,比如 OpenGL、Metal 等

TGFX 引擎是如何实现纹理绘制?本文基于 OpenGL 图形库分析讲解 TGFX 渲染框架分层及详细架构设计。开始之前,先提一个问题:

绘制一个 Texture 纹理对象,一般需要经历哪些过程?

渲染流程

通常情况下,绘制一个 Texture 纹理对象到目标 Layer 上,可以抽象为以下几个阶段:

1. 获取上下文:通过 EGL 获取 Context 绘制上下文,提供与渲染设备交互的能力,比如缓冲区交换、Canvas 及 Paint 交互等

2. 定义着色器:基于 OpenGL 的着色器语言(GLSL)编写着色器代码,编写自定义顶点着色器和片段着色器代码,编译、链接加载和使用它们

3. 绑定数据源:基于渲染坐标系几何计算绑定顶点数据,加载并绑定纹理对象给 GPU,设置渲染目标、混合模式等

4. 渲染执行:提交渲染命令给渲染线程,转化为底层图形 API 调用、并执行实际的渲染操作



关于 OpenGL 完整的渲染流程,网上有比较多的资料介绍,在此不再赘述,有兴趣的同学可以参考 OpenGL ES Pipeline

框架层级

TGFX 框架大致可分为三大块:

1. Drawable 上下文:基于 EGL 创建 OpenGL 上下文,提供与渲染设备交互的能力

2. Canvas 接口:定义画布 Canvas 及画笔 Paint,对外提供渲染接口、记录渲染状态以及创建绘制任务等

3. DrawOp 执行:定义并装载着色器函数,绑定数据源,执行实际渲染操作

为了支持多平台,TGFX 定义了一套完整的框架基类,实现框架与平台的物理隔离,比如矩阵对象 Matrix、坐标 Rect 等,应用上层负责平台对象与 TFGX 对象的映射转化

- (void)setMatrix:(CGAffineTransform)value {  pag::Matrix matrix = {};  matrix.setAffine(value.a, value.b, value.c, value.d, value.tx, value.ty);  _pagLayer->setMatrix(matrix);}
复制代码

Drawable 上下文

PAG 通过抽象 Drawable 对象,封装了绘制所需的上下文,其主要包括以下几个对象

1. Device(设备):作为硬件设备层,负责与渲染设备交互,比如创建维护 EAGLContext 等

2. Window(窗口):拥有一个 Surface,负责图形库与绘制目标的绑定,比如将的 opengl 的 renderBuffer 绑定到 CAEAGLLayer 上;

3. Surface(表面):创建 canvas 画布提供可绘制区域,对外提供 flush 绘制接口;当窗口尺寸发生变化时,surface 会创建新的 canvas

4. Canvas(画布):作为实际可绘制区域,提供绘制 api,进行实际的绘图操作,比如绘制一个 image 或者 shape 等



详细代码如下:

1、Device 创建 Context
std::shared_ptr<GLDevice> GLDevice::Make(void* sharedContext) {  if (eaglShareContext != nil) {    eaglContext = [[EAGLContext alloc] initWithAPI:[eaglShareContext API]                                        sharegroup:[eaglShareContext sharegroup]];  } else {    // 创建Context    eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];    if (eaglContext == nil) {      eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];    }  }  auto device = EAGLDevice::Wrap(eaglContext, false);  return device;}
std::shared_ptr<EAGLDevice> EAGLDevice::Wrap(EAGLContext* eaglContext, bool isAdopted) { auto oldEAGLContext = [[EAGLContext currentContext] retain]; if (oldEAGLContext != eaglContext) { auto result = [EAGLContext setCurrentContext:eaglContext]; if (!result) { return nullptr; } } auto device = std::shared_ptr<EAGLDevice>(new EAGLDevice(eaglContext), EAGLDevice::NotifyReferenceReachedZero); if (oldEAGLContext != eaglContext) { [EAGLContext setCurrentContext:oldEAGLContext]; } return device;}
// 获取Contextbool EAGLDevice::makeCurrent(bool force) { oldContext = [[EAGLContext currentContext] retain]; if (oldContext == _eaglContext) { return true; } if (![EAGLContext setCurrentContext:_eaglContext]) { oldContext = nil; return false; } return true;}
复制代码
2、Window 创建 Surface,绑定 RenderBuffer
std::shared_ptr<Surface> EAGLWindow::onCreateSurface(Context* context) {  auto gl = GLFunctions::Get(context);  ...  gl->genFramebuffers(1, &frameBufferID);  gl->bindFramebuffer(GL_FRAMEBUFFER, frameBufferID);  gl->genRenderbuffers(1, &colorBuffer);  gl->bindRenderbuffer(GL_RENDERBUFFER, colorBuffer);  gl->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);  auto eaglContext = static_cast<EAGLDevice*>(context->device())->eaglContext();  // 绑定到CAEAGLLayer上  [eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];  ...  GLFrameBufferInfo glInfo = {};  glInfo.id = frameBufferID;  glInfo.format = GL_RGBA8;  BackendRenderTarget renderTarget = {glInfo, static_cast<int>(width), static_cast<int>(height)};  // 创建Surface  return Surface::MakeFrom(context, renderTarget, ImageOrigin::BottomLeft);}
// 通过renderTarget持有context、frameBufferID及Sizestd::shared_ptr<Surface> Surface::MakeFrom(Context* context, const BackendRenderTarget& renderTarget, ImageOrigin origin, const SurfaceOptions* options) { auto rt = RenderTarget::MakeFrom(context, renderTarget, origin); return MakeFrom(std::move(rt), options);}
复制代码
3、Surface 创建 Canvas 及 flush 绘制
Canvas* Surface::getCanvas() {  // 尺寸变化时会清空并重新创建canvas  if (canvas == nullptr) {    canvas = new Canvas(this);  }  return canvas;}
bool Surface::flush(BackendSemaphore* signalSemaphore) { auto semaphore = Semaphore::Wrap(signalSemaphore); // drawingManager创建tasks,装载绘制pipiline renderTarget->getContext()->drawingManager()->newTextureResolveRenderTask(this); auto result = renderTarget->getContext()->drawingManager()->flush(semaphore.get()); return result;}
复制代码
4、渲染流程
bool PAGSurface::draw(RenderCache* cache, std::shared_ptr<Graphic> graphic,                      BackendSemaphore* signalSemaphore, bool autoClear) {  // 获取context上下文                      auto context = lockContext(true);  // 获取surface  auto surface = drawable->getSurface(context);  // 通过canvas画布  auto canvas = surface->getCanvas();  // 执行实际绘制  onDraw(graphic, surface, cache);  // 调用flush  surface->flush();  // glfinish  context->submit();  // 绑定GL_RENDERBUFFER  drawable->present(context);  // 释放context上下文  unlockContext();  return true;}
复制代码

Canvas 接口

Canvas API 主要包括画布操作及对象绘制两大类:

画布操作包括 Matrix 矩阵变化、Blend 融合模式、画布裁切等设置,通过对 canvasState 画布状态的操作实现绘制上下文的切换

对象绘制包括 Path、Shape、Image 以及 Glyph 等对象的绘制,结合 Paint 画笔实现纹理、文本、图形、蒙版等多种形式的绘制及渲染

class Canvas { 	// 画布操作  void setMatrix(const Matrix& matrix);  void setAlpha(float newAlpha);  void setBlendMode(BlendMode blendMode);
// 绘制API void drawRect(const Rect& rect, const Paint& paint); void drawPath(const Path& path, const Paint& paint); void drawShape(std::shared_ptr<Shape> shape, const Paint& paint); void drawImage(std::shared_ptr<Image> image, const Matrix& matrix, const Paint* paint = nullptr); void drawGlyphs(const GlyphID glyphIDs[], const Point positions[], size_t glyphCount, const Font& font, const Paint& paint);};
复制代码


// CanvasState记录当前画布的状态,包括Alph、blend模式、变化矩阵等struct CanvasState {  float alpha = 1.0f;  BlendMode blendMode = BlendMode::SrcOver;  Matrix matrix = Matrix::I();  Path clip = {};  uint32_t clipID = kDefaultClipID;};
// 通过save及restore实现绘制状态的切换void Canvas::save() { auto canvasState = std::make_shared<CanvasState>(); *canvasState = *state; savedStateList.push_back(canvasState);}
void Canvas::restore() { if (savedStateList.empty()) { return; } state = savedStateList.back(); savedStateList.pop_back();}
复制代码

DrawOp 执行

DrawOp 负责实际的绘制逻辑,比如 OpenGL 着色器函数的创建装配、顶点及纹理数据的创建及绑定等

TGFX 抽象了 FillRectOp 矩形绘制 Op,可以覆盖绝大多数场景的绘制需求

当然,其还支持其它类型的绘制 Op,比如 ClearOp 清屏、TriangulatingPathOp 三角图形绘制 Op 等

class DrawOp : public Op {  // DrawOp通过Pipiline实现多个_colors纹理对象及_masks蒙版的绘制  std::vector<std::unique_ptr<FragmentProcessor>> _colors;  std::vector<std::unique_ptr<FragmentProcessor>> _masks;};
// 矩形实际绘制执行者class FillRectOp : public DrawOp { FillRectOp(std::optional<Color> color, const Rect& rect, const Matrix& viewMatrix, const Matrix& localMatrix); void onPrepare(Gpu* gpu) override; void onExecute(OpsRenderPass* opsRenderPass) override;};
复制代码

总结

本文结合 OpenGL 讲解了 TGFX 渲染引擎的大概框架结构,让各位有了一个初步认知

接下来将结合 image 纹理绘制介绍 TGFX 渲染引擎详细的绘制渲染流程,欢迎大家关注点赞!

发布于: 2 小时前阅读数: 3
用户头像

olinone

关注

还未添加个人签名 2019-04-10 加入

还未添加个人简介

评论

发布
暂无评论
PAG动效框架源码笔记 (四)渲染框架_ios_olinone_InfoQ写作社区