注册
iOS

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

前言


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


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


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


渲染流程


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


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


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


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


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


bc5bd7f48394a90251c0a13b95887c48.png


关于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等


6b5684cdf41e43c383588b0ce28e9aa8.png


详细代码如下:


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;
}

// 获取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
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绘制
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渲染引擎详细的绘制渲染流程,欢迎大家关注点赞!


作者:olinone
链接:https://juejin.cn/post/7239178749153312824
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册