前言
PAG采用自研TGFX特效渲染引擎,抽象分离了接口及平台实现类,可以扩展支持多种图形渲染库,比如OpenGL、Metal等
TGFX引擎是如何实现纹理绘制?本文基于OpenGL图形库分析讲解TGFX渲染框架分层及详细架构设计。开始之前,先提一个问题:
渲染流程
通常情况下,绘制一个Texture纹理对象到目标Layer上,可以抽象为以下几个阶段:
1. 获取上下文:通过EGL获取Context绘制上下文,提供与渲染设备交互的能力,比如缓冲区交换、Canvas及Paint交互等
2. 定义着色器:基于OpenGL的着色器语言(GLSL)编写着色器代码,编写自定义顶点着色器和片段着色器代码,编译、链接加载和使用它们
3. 绑定数据源:基于渲染坐标系几何计算绑定顶点数据,加载并绑定纹理对象给GPU,设置渲染目标、混合模式等
4. 渲染执行:提交渲染命令给渲染线程,转化为底层图形API调用、并执行实际的渲染操作
关于OpenGL完整的渲染流程,网上有比较多的资料介绍,在此不再赘述,有兴趣的同学可以参考
TGFX框架大致可分为三大块:
1. Drawable上下文:基于EGL创建OpenGL上下文,提供与渲染设备交互的能力
2. Canvas接口:定义画布Canvas及画笔Paint,对外提供渲染接口、记录渲染状态以及创建绘制任务等
3. DrawOp执行:定义并装载着色器函数,绑定数据源,执行实际渲染操作
为了支持多平台,TGFX定义了一套完整的框架基类,实现框架与平台的物理隔离,比如矩阵对象Matrix、坐标Rect等,应用上层负责平台对象与TFGX对象的映射转化
1 2 3 4 5 |
- (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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
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; } // 获取Context bool 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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及Size std::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绘制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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、渲染流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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接口
画布操作包括Matrix矩阵变化、Blend融合模式、画布裁切等设置,通过对canvasState画布状态的操作实现绘制上下文的切换
对象绘制包括Path、Shape、Image以及Glyph等对象的绘制,结合Paint画笔实现纹理、文本、图形、蒙版等多种形式的绘制及渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 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执行
TGFX抽象了FillRectOp矩形绘制Op,可以覆盖绝大多数场景的绘制需求
当然,其还支持其它类型的绘制Op,比如ClearOp清屏、TriangulatingPathOp三角图形绘制Op等
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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渲染引擎详细的绘制渲染流程,欢迎大家关注点赞!