PAG动效框架源码笔记 (五)渲染流程

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

前言

上一章介绍了TGFX渲染框架的大致结构,本章基于OpenGL介绍TGFX绘制Texture纹理详细的渲染流程

绘制Texture纹理,渲染引擎主要包括两个流程:GLSL着色器代码的装载及数据对象的绑定操作

着色器代码(GLSL)

渲染一个纹理,TGFX 需要构建顶点着色器(Vertex Shader)和片段着色器(Frament Shader)两个着色器,其代码分别如下

#version 100
precision mediump float;
uniform vec4 tgfx_RTAdjust; // 坐标系映射矩阵
uniform mat3 uCoordTransformMatrix_0_Stage0; 
​
attribute vec2 aPosition;
attribute vec2 localCoord;
attribute vec4 inColor;
​
varying vec2 vTransformedCoords_0_Stage0;
varying vec4 vColor_Stage0;
​
void main() {
    // Geometry Processor QuadPerEdgeAAGeometryProcessor
    // 矩阵变化,比如缩放、偏移,更适合GPU并行计算
    vTransformedCoords_0_Stage0 = (uCoordTransformMatrix_0_Stage0 * vec3(localCoord, 1)).xy;
    vColor_Stage0 = inColor;
    // 坐标系转化
    gl_Position = vec4(aPosition.xy * tgfx_RTAdjust.xz + tgfx_RTAdjust.yw, 0, 1); 
}

顶点着色器需要计算每个顶点在渲染坐标系中的坐标,同时将纹理数据输出给片段着色器

为了优化计算性能,TGFX没有在CPU阶段处理矩阵变化和坐标映射,而是交由GPU来处理( GPU更适合矩阵计算);同时,由GPU处理坐标系映射可以更灵活适配不同平台不同坐标系

#version 100
precision mediump float;
uniform mat3 uMat3ColorConversion_Stage1; // 颜色空间转化矩阵
uniform vec2 uAlphaStart_Stage1; // Alpha区域偏移量
uniform sampler2D uTextureSampler_0_Stage1;
uniform sampler2D uTextureSampler_1_Stage1;
​
varying highp vec2 vTransformedCoords_0_Stage0;
varying highp vec4 vColor_Stage0;
​
void main() {
    vec4 outputColor_Stage0;
    vec4 outputCoverage_Stage0;
    { // Stage 0 QuadPerEdgeAAGeometryProcessor
        outputCoverage_Stage0 = vec4(1.0);
        outputColor_Stage0 = vColor_Stage0;
    }
    vec4 output_Stage1;
    { // Stage 1 XfermodeFragmentProcessor - dst
        vec4 child;
        {
            // Child Index 0 (mangle: _c0): YUVTextureEffect
            // yuv取值,光栅化后每个点坐标都不一样
            vec3 yuv;
            yuv.x = texture2D(uTextureSampler_0_Stage1, vTransformedCoords_0_Stage0).rrra.r;
            yuv.yz = texture2D(uTextureSampler_1_Stage1, vTransformedCoords_0_Stage0).rgrg.ra;
            yuv.x -= (16.0 / 255.0);
            yuv.yz -= vec2(0.5, 0.5);
            // yuv数据转rgb
            vec3 rgb = clamp(uMat3ColorConversion_Stage1 * yuv, 0.0, 1.0); 
            // 通过RGB颜色区域偏移计算Alpha区域,比如左rgb右alpha,整体+0.5
            vec2 alphaVertexColor = vTransformedCoords_0_Stage0 + uAlphaStart_Stage1; 
            float yuv_a = texture2D(uTextureSampler_0_Stage1, alphaVertexColor).rrra.r;
            // 为避免因压缩误差、精度等原因造成不透明变成部分透明(比如255变成254),
            // 下面进行了减1.0/255.0的精度修正。
            yuv_a = (yuv_a - 16.0/255.0) / (219.0/255.0 - 1.0/255.0);
            yuv_a = clamp(yuv_a, 0.0, 1.0); 
            child = vec4(rgb * yuv_a, yuv_a) * vec4(1.0);
        }
        // Compose Xfer Mode: DstIn
        output_Stage1 = child * outputColor_Stage0.a; // blend混合模式
    }
    { // Xfer Processor EmptyXferProcessor
        gl_FragColor = output_Stage1 * outputCoverage_Stage0;
    }
}

片段着色器决定了光栅化后每个点像素的最终颜色,TGFX需要处理纹理RGBA计算、Mask蒙版遮罩以及多图层的blend混合计算等

渲染流程

1、任务创建
void Canvas::drawImage(std::shared_ptr<Image> image, const Paint* paint) {
  // Mipmap纹理映射处理
  auto mipMapMode = image->hasMipmaps() ? tgfx::MipMapMode::Linear : tgfx::MipMapMode::None;
  tgfx::SamplingOptions sampling(tgfx::FilterMode::Linear, mipMapMode);
  drawImage(std::move(image), sampling, paint);
}
​
void Canvas::drawImage(std::shared_ptr<Image> image, SamplingOptions sampling, const Paint* paint) {
  ...
  // 记录Canvas当前状态
  auto oldMatrix = getMatrix();
  ...
  // 绘制image(包含解码后的texture纹理)
  drawImage(std::move(image), sampling, paint);
  // 还原Canvas上下文
  setMatrix(oldMatrix);
}
​
void Canvas::drawImage(std::shared_ptr<Image> image, SamplingOptions sampling, const Paint& paint) {
  ...
  // 纹理处理,序列帧左rgb右alpha数据
  auto processor = image->asFragmentProcessor(getContext(), surface->options()->flags(), sampling);
  ...
  // 创建画笔Paint
  if (!PaintToGLPaintWithImage(getContext(), surface->options()->flags(), paint, state->alpha, std::move(processor), image->isAlphaOnly(), &glPaint)) {
    return;
  }
  // 创建矩形填充绘制Operation
  auto op = FillRectOp::Make(glPaint.color, localBounds, state->matrix);
  // 绑定Paint到Op上,提交绘制Task
  draw(std::move(op), std::move(glPaint), true);
}
​
// 创建Paint
static bool PaintToGLPaint(Context* context, uint32_t surfaceFlags, const Paint& paint, float alpha, std::unique_ptr<FragmentProcessor> shaderProcessor, GpuPaint* glPaint) {
  ...
  // 绘制串行Pipeline
  // 纹理
  shaderFP = shader->asFragmentProcessor(args);
  if (shaderFP) {
    glPaint->colorFragmentProcessors.emplace_back(std::move(shaderFP));
  } 
  // 滤镜
  if (auto colorFilter = paint.getColorFilter()) {
    if (auto processor = colorFilter->asFragmentProcessor()) {
      glPaint->colorFragmentProcessors.emplace_back(std::move(processor));
    } 
  }
  // 蒙版遮罩
  if (auto maskFilter = paint.getMaskFilter()) {
    if (auto processor = maskFilter->asFragmentProcessor(args)) {
      glPaint->coverageFragmentProcessors.emplace_back(std::move(processor));
    }
  }
  return true;
}
​
// 绑定Paint到绘制Operation中,提交到绘制OperationQueue
void Canvas::draw(std::unique_ptr<DrawOp> op, GpuPaint paint, bool aa) {
  ...
  // Canvas裁切
  auto 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);
  BlendModeCoeff first;
  BlendModeCoeff second;
  // blend混合模式
  if (BlendModeAsCoeff(state->blendMode, &first, &second)) {
    op->setBlendFactors(std::make_pair(first, second));
  } else {
    op->setXferProcessor(PorterDuffXferProcessor::Make(state->blendMode));
    op->setRequireDstTexture(!getContext()->caps()->frameBufferFetchSupport);
  }
  op->setAA(aaType);
  // 纹理图层
  op->setColors(std::move(paint.colorFragmentProcessors));
  // 蒙版图层
  op->setMasks(std::move(masks));
  surface->aboutToDraw(false);
  // 加入到绘制队列中
  drawContext->addOp(std::move(op));
}
​
void SurfaceDrawContext::addOp(std::unique_ptr<Op> op) {
  getOpsTask()->addOp(std::move(op));
}
2、Flush绘制
bool DrawingManager::flush(Semaphore* signalSemaphore) {
  ...
  // 遍历执行
  std::for_each(tasks.begin(), tasks.end(), [gpu](std::shared_ptr<RenderTask>& task) { task->execute(gpu); });
  return context->caps()->semaphoreSupport && gpu->insertSemaphore(signalSemaphore);
}
​
bool OpsTask::execute(Gpu* gpu) {
  // 先prepare
  std::for_each(ops.begin(), ops.end(), [gpu](auto& op) { op->prepare(gpu); });
  // 再execute
  opsRenderPass->begin();
  auto tempOps = std::move(ops);
  for (auto& op : tempOps) {
    op->execute(opsRenderPass);
  }
  opsRenderPass->end();
  // 提交
  gpu->submit(opsRenderPass);
  return true;
}
3、绘制预处理
// 顶点着色器数据构造
void FillRectOp::onPrepare(Gpu* gpu) {
  // 数据构造(CPU),包含画布、纹理以及RGB区域数据
  auto data = vertices();
  // 绑定数据到GPU
  vertexBuffer = GpuBuffer::Make(gpu->context(), BufferType::Vertex, data.data(), data.size() * sizeof(float));
  // 自定义绘制顺序index
  if (aa == AAType::Coverage) {
    indexBuffer = gpu->context()->resourceProvider()->aaQuadIndexBuffer();
  } else {
    indexBuffer = gpu->context()->resourceProvider()->nonAAQuadIndexBuffer();
  }
}
​
std::shared_ptr<GpuBuffer> GpuBuffer::Make(Context* context, BufferType bufferType, const void* buffer, size_t size) {
  ...
  auto glBuffer = std::static_pointer_cast<GLBuffer>(context->resourceCache()->findScratchResource(scratchKey));
  ...
  // GPU数据绑定
  gl->bindBuffer(target, glBuffer->_bufferID);
  // GPU数据赋值
  gl->bufferData(target, static_cast<GLsizeiptr>(size), buffer, GL_STATIC_DRAW);
  return glBuffer;
}
4、绘制执行
void FillRectOp::onExecute(OpsRenderPass* opsRenderPass) {
  // 着色器代码定义
  auto info = createProgram(opsRenderPass, QuadPerEdgeAAGeometryProcessor::Make(opsRenderPass->renderTarget()->width(), opsRenderPass->renderTarget()->height(), aa, !colors.empty()));
  // 着色器代码装载及数据绑定
  opsRenderPass->bindPipelineAndScissorClip(info, scissorRect());
  // 绑定顶点及自定义绘制顺序数据
  opsRenderPass->bindBuffers(indexBuffer, vertexBuffer);
  if (needsIndexBuffer()) {
    // 自定义顺序绘制
    opsRenderPass->drawIndexed(PrimitiveType::Triangles, 0, static_cast<int>(rects.size()) * numIndicesPerQuad);
  } else {
    // 默认顺序绘制
    opsRenderPass->draw(PrimitiveType::TriangleStrip, 0, 4);
  }
}
​
// 着色器代码生成,包括顶点和片段着色器代码
ProgramInfo DrawOp::createProgram(OpsRenderPass* opsRenderPass,
                                  std::unique_ptr<GeometryProcessor> gp) {
  auto numColorProcessors = _colors.size();
  // 片段着色器函数代码Pipeline组装
  std::vector<std::unique_ptr<FragmentProcessor>> fragmentProcessors = {};
  fragmentProcessors.resize(numColorProcessors + _masks.size());
  // 纹理
  std::move(_colors.begin(), _colors.end(), fragmentProcessors.begin());
  // 蒙版
  std::move(_masks.begin(), _masks.end(),
            fragmentProcessors.begin() + static_cast<int>(numColorProcessors));
  ...
  ProgramInfo info;
  // blend模式
  info.blendFactors = _blendFactors;
  info.pipeline = std::make_unique<Pipeline>(std::move(fragmentProcessors), numColorProcessors, std::move(_xferProcessor), dstTexture, dstTextureOffset, &swizzle);
  info.pipeline->setRequiresBarrier(dstTexture != nullptr && dstTexture == opsRenderPass->renderTargetTexture());
  // 顶点着色器函数代码
  info.geometryProcessor = std::move(gp);
  return info;
}
​
// 着色器代码装载,包括编译、链接及Uniform数据绑定
bool GLOpsRenderPass::onBindPipelineAndScissorClip(const ProgramInfo& info, const Rect& drawBounds) {
  GLProgramCreator creator(info.geometryProcessor.get(), info.pipeline.get());
  // Program函数创建,先缓存,没有再新建
  _program = static_cast<GLProgram*>(_context->programCache()->getProgram(&creator));
  auto glRT = static_cast<GLRenderTarget*>(_renderTarget.get());
  auto* program = static_cast<GLProgram*>(_program);
  // 绑定函数
  gl->useProgram(program->programID());
  gl->bindFramebuffer(GL_FRAMEBUFFER, glRT->getFrameBufferID());
  gl->viewport(0, 0, glRT->width(), glRT->height());
  // GL裁切
  UpdateScissor(_context, drawBounds);
  // GL混合模式
  UpdateBlend(_context, info.blendFactors);
  // 绑定数据,包括Uniform参数和纹理数据
  program->updateUniformsAndTextureBindings(glRT, *info.geometryProcessor, *info.pipeline);
  return true;
}
​
// 创建Program函数
std::unique_ptr<GLProgram> GLProgramBuilder::CreateProgram(Context* context, const GeometryProcessor* geometryProcessor, const Pipeline* pipeline) {
  GLProgramBuilder builder(context, geometryProcessor, pipeline);
  if (!builder.emitAndInstallProcessors()) {
    return nullptr;
  }
  return builder.finalize();
}
​
bool ProgramBuilder::emitAndInstallProcessors() {
  // 生成顶点着色器代码
  emitAndInstallGeoProc(&inputColor, &inputCoverage);
  // 生成片段着色器代码
  emitAndInstallFragProcessors(&inputColor, &inputCoverage);
  // 图层叠加混合代码
  emitAndInstallXferProc(inputColor, inputCoverage);
  emitFSOutputSwizzle();
  return checkSamplerCounts();
}
​
std::unique_ptr<GLProgram> GLProgramBuilder::finalize() {
  ...
  // Vertex Shader代码
  auto vertex = vertexShaderBuilder()->shaderString();
  // Frament Shader代码
  auto fragment = fragmentShaderBuilder()->shaderString();
  // 创建Program,编译、链接
  auto programID = CreateGLProgram(context, vertex, fragment);
  // GPU顶点着色器参数绑定
  computeCountsAndStrides(programID);
  // 获取Program Uniform位置
  resolveProgramResourceLocations(programID);
  return createProgram(programID);
}
​
std::unique_ptr<GLProgram> GLProgramBuilder::createProgram(unsigned programID) {
  auto program = new GLProgram(context, uniformHandles, programID, _uniformHandler.uniforms, std::move(glGeometryProcessor), std::move(xferProcessor), std::move(fragmentProcessors), attributes, vertexStride);
  // GPU Uniform参数绑定
  program->setupSamplerUniforms(_uniformHandler.samplers);
  return std::unique_ptr<GLProgram>(program);
}
​
// 提交绘制
void GLOpsRenderPass::draw(const std::function<void()>& func) {
  // GPU顶点着色器参数赋值
  gl->bindBuffer(GL_ARRAY_BUFFER, std::static_pointer_cast<GLBuffer>(_vertexBuffer)->bufferID());
  auto* program = static_cast<GLProgram*>(_program);
  for (const auto& attribute : program->vertexAttributes()) {
    const AttribLayout& layout = GetAttribLayout(attribute.gpuType);
    gl->vertexAttribPointer(static_cast<unsigned>(attribute.location), layout.count, layout.type, layout.normalized, program->vertexStride(), reinterpret_cast<void*>(attribute.offset));
    gl->enableVertexAttribArray(static_cast<unsigned>(attribute.location));
  }
  // 绘制
  func();
  ...
}

总结

为了支持多平台不同对象(纹理、图形等)绘制,TGFX抽象封装了一套完整的GLSL代码生成模版,各平台继承模版父类负责逻辑实现,后续可以针对iOS平台提供Metal绘制实现

class ProgramCreator {
 public:
  virtual ~ProgramCreator() = default;
  virtual void computeUniqueKey(Context* context, BytesKey* uniqueKey) const = 0;
  virtual std::unique_ptr<Program> createProgram(Context* context) const = 0;
};
​
class GLProgramCreator : public ProgramCreator {
 public:
  GLProgramCreator(const GeometryProcessor* geometryProcessor, const Pipeline* pipeline);
  void computeUniqueKey(Context* context, BytesKey* uniqueKey) const override;
  std::unique_ptr<Program> createProgram(Context* context) const override;
};
​
std::unique_ptr<Program> GLProgramCreator::createProgram(Context* context) const {
  return GLProgramBuilder::CreateProgram(context, geometryProcessor, pipeline);
}

至此, PAG动效框架源码就全部讲解完成,欢迎大家关注点赞

框架中还有大量本文未提到的内容,比如滤镜、文本、图形绘制等等,有兴趣的同学建议阅读源码

相信未来行业内会有大量类似的解决方案,比如基于MP4方案的多图层绘制框架等

全部评论

相关推荐

专心打鱼:互联网搬运工,贴子都要偷
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务