转载请注明出处: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计划的多图层制作结构等