2021SC@SDUSC
一、简述
我们继续之前的博客分析2D渲染服务后续的内容。边学习边分析渲染流程。
二、分析
接下来一些服务主要提供对于变换矩阵、颜色等熟悉的操作,诸如入栈、弹出等,这里不再赘述。
我们先看一下在之前分析的结束渲染时进行的批处理元素的刷新:
DrawBatch(batchStart, batchSize);
对于DrawBatch()这个函数:
const Render2DDrawCall& d = DrawCalls[startIndex];GPUBuffer* vb = VB.GetBuffer();GPUBuffer* ib = IB.GetBuffer();uint32 countIb = 0;for (int32 i = 0; i < count; i++)countIb += DrawCalls[startIndex + i].CountIB;
startIndex 是函数的第一个参数batchStart,首先根据该参数获取绘图调用对象;
VB是允许在单帧期间渲染任何顶点的动态顶点缓冲区(支持动态调整大小);IB是允许在单帧期间呈现任何索引的动态索引缓冲区(支持动态调整大小);它们都继承自DynamicBuffer,是允许在单帧期间更新和使用 GPU 数据(索引/顶点/其他)的动态 GPU 缓冲区(支持动态调整大小)。
循环中的count为函数的第二个参数,这里使用countIb记录总的IB大小。
if (d.Type == DrawCallType::ClipScissors){Rectangle* scissorsRect = (Rectangle*)&d.AsClipScissors.X;Context->SetScissor(*scissorsRect);IsScissorsRectEmpty = scissorsRect->Size.IsAnyZero();return;}if (IsScissorsRectEmpty)return;
判断上面获取的绘图调用的类型,枚举的绘图调用类型如下:
enum class DrawCallType : byte
{FillRect,FillRectNoAlpha,FillRT,FillTexture,FillTexturePoint,DrawChar,DrawCharMaterial,Custom,Material,Blur,ClipScissors,LineAA,MAX
};
这里首先判断绘图调用类型是否为剪刀,接下来获取绘图调用中的子结构体AsClipScissors的X属性,并设置剪刀矩形。获取值IsScissorsRectEmpty,该值指示任何向量分量是否为零。
判断剪刀为特殊情况,下面根据绘图调用类型的不同,对Context进行不同的设置:
switch (d.Type){case DrawCallType::FillRect:Context->SetState(CurrentPso->PS_Color);break;case DrawCallType::FillRectNoAlpha:Context->SetState(CurrentPso->PS_Color_NoAlpha);break;case DrawCallType::FillRT:Context->BindSR(0, d.AsRT.Ptr);Context->SetState(CurrentPso->PS_Image);break;case DrawCallType::FillTexture:Context->BindSR(0, d.AsTexture.Ptr);Context->SetState(CurrentPso->PS_Image);break;case DrawCallType::FillTexturePoint:Context->BindSR(0, d.AsTexture.Ptr);Context->SetState(CurrentPso->PS_ImagePoint);break;case DrawCallType::DrawChar:Context->BindSR(0, d.AsChar.Tex);Context->SetState(CurrentPso->PS_Font);break;
BindSR所做的工作是将纹理绑定到着色器资源槽。
这里我们重点看一下绘图调用类型 绘制字符材质 时所做的工作:
case DrawCallType::DrawCharMaterial:{// 应用和绑定材料auto material = d.AsChar.Mat;MaterialBase::BindParameters bindParams(Context, *(RenderContext*)nullptr);bindParams.CustomData = &ViewProjection;material->Bind(bindParams);// 绑定字体图集作为材质参数static StringView FontParamName = TEXT("Font");auto param = material->Params.Get(FontParamName);if (param && param->GetParameterType() == MaterialParameterType::Texture){Context->BindSR(param->GetRegister(), d.AsChar.Tex);}// 绑定索引和顶点缓冲区Context->BindIB(ib);Context->BindVB(ToSpan(&vb, 1));// 绘制Context->DrawIndexed(countIb, 0, d.StartIB);// 恢复管道(材料应用覆盖它)const auto cb = GUIShader->GetShader()->GetCB(0);Context->BindCB(0, cb);return;}
当绘图调用的类型为Material 材质时,流程与 绘制字符材质 大致相同:
case DrawCallType::Material:
绘图调用类型为 模糊 时:
const Vector4 bounds(d.AsBlur.UpperLeftX, d.AsBlur.UpperLeftY, d.AsBlur.BottomRightX, d.AsBlur.BottomRightY);float blurStrength = Math::Max(d.AsBlur.Strength, 1.0f);auto& limits = GPUDevice::Instance->Limits;int32 renderTargetWidth = Math::Min(Math::RoundToInt(d.AsBlur.Width), limits.MaximumTexture2DSize);int32 renderTargetHeight = Math::Min(Math::RoundToInt(d.AsBlur.Height), limits.MaximumTexture2DSize);
根据调用的参数定义边界 bounds,blurStrength模糊强度,由GPU实例对象获取GPU限制,
渲染目标宽度和长度取 绘图调用传来的参数 和 GPU限制的2D 纹理的最大尺寸 两者中的最小值
int32 kernelSize = 0, downSample = 0;CalculateKernelSize(blurStrength, kernelSize, downSample);if (downSample > 0){renderTargetWidth = Math::DivideAndRoundUp(renderTargetWidth, downSample);renderTargetHeight = Math::DivideAndRoundUp(renderTargetHeight, downSample);blurStrength /= downSample;}
初始化内核大小和下采样为0,再以上面获取的模糊强度为参数计算内核大小,这里具体计算方法我们不在分析。然后判断下采样值,若大于零,则根据下采样的值改变渲染目标的宽度和高度以及模糊强度,这里DivideAndRoundUp()将两个整数相除并向上取整。
对于模糊,下面还有一系列操作,这里就不再放上代码,而是简单介绍一下进行了哪些操作:
- 如果没有机会渲染任何内容,则返回;
- 获取临时纹理
- 准备模糊数据
- 缩小(或不缩小)并提取背景图像以进行模糊处理
- 渲染模糊(第一遍)
- 渲染模糊(第二遍)
- 恢复输出
- 将最终模糊绘制链接为纹理
- 释放
这次的分析就到这里为止。