游戏引擎从零开始(38)-Batch Rendering(4)

分批处理的应用

批处理是不是真的有性能优化呢?

这章我们绘制更多的图元,对比批渲染和多次渲染的效果。

最后实现如下图所示的效果,在上一章的基础上更加了很多小矩形,并增加了统计数据的显示。

游戏引擎从零开始(38)-Batch Rendering(4)

增加分批 & 统计

代码修改不多,最核心的修改是,根据MaxQuads自动刷新,超过上限就触发一次绘制。

Hazel/src/Hazel/Renderer/Renderer2D.h

// Stats
struct Statistics
{
  uint32_t DrawCalls = 0;
  uint32_t QuadCount = 0;

  uint32_t GetTotalVertexCount() { return QuadCount * 4; }
  uint32_t GetTotalIndexCount() { return QuadCount * 6; }
};

// 每帧绘制结束重置统计数据
static void ResetStats();
static Statistics GetStats();
private:
static void FlushAndReset();

Sandbox/src/Sandbox2D.cpp

struct Renderer2DData
{
  static const uint32_t MaxQuads = 10000;
  static const uint32_t MaxVertices = MaxQuads * 4;
  static const uint32_t MaxIndices = MaxQuads * 6;
  
  ...
  // 增加统计数据
  Renderer2D::Statistics Stats;
}

void Renderer2D::Flush()
{
  ...
  // 刷新结束增加DrawCall统计
  s_Data->Stats.DrawCalls++;
}

// FlushAndReset实现,刷新后重置s_Data,新启一个批次
void Renderer2D::FlushAndReset() {
    EndScene();

    s_Data->QuadIndexCount = 0;
    s_Data->QuadVertexBufferPtr = s_Data->QuadVertexBufferBase;

    s_Data->TextureSlotIndex = 1;
}

// DrawQuad中累计数据超过上限则触发刷新和重置
// 其他DrawQuad均有同样的代码逻辑,此处仅展示一个DrawQuad的代码变化,完整代码参考文末github链接
void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const glm::vec4 &color) {
    HZ_PROFILE_FUNCTION();

    // s_Data中累计的数据超过单批绘制的上限,则触发刷新和重置
    if (s_Data->QuadIndexCount >= Renderer2DData::MaxIndices) {
        FlushAndReset();
    }
    
    ...
    // DrawQuad结束,累计QuadCount值
    s_Data->Stats.QuadCount++;
}


// 重置统计数据
void Renderer2D::ResetStats() {
    // 注意这里哟花姑娘memset的方式重置数据为0
    memset(&s_Data->Stats, 0, sizeof(Statistics));
}

// 获取统计数据
Renderer2D::Statistics Renderer2D::GetStats() {
    return s_Data->Stats;
}
    

Demo工程中的修改
Sandbox/src/Sandbox2D.cpp

void Sandbox2D::OnUpdate(Hazel::Timestep ts) {
    // Update
    m_CameraController.OnUpdate(ts);
    // 每帧重置统计数据
    Hazel::Renderer2D::ResetStats();
    
    
    ...
    // 增加测试数据
    // 绘制10 * 10的矩形彩色棋盘
    Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
for(float y = -5.0f; y < 5.0f; y += 0.5f) {
    for (float x = -5.0f; x < 5.0f; x += 0.5f) {
        glm::vec4 color = {(x+5.0f)/10.0f, 0.4f, (y+5.0f)/10.0f, 0.7f};
        Hazel::Renderer2D::DrawQuad({x, y}, {0.45f, 0.45f}, color);
    }
}
Hazel::Renderer2D::EndScene();
}

// ImGui实现统计面板
void Sandbox2D::OnImGuiRender() {
    HZ_PROFILE_FUNCTION();

    ImGui::Begin("Settings");

    auto stats = Hazel::Renderer2D::GetStats();
    ImGui::Text("Renderer2D Stats:");
    ImGui::Text("Draw Calls : %d", stats.DrawCalls);
    ImGui::Text("Quads : %d", stats.QuadCount);
    ImGui::Text("Vertices : %d", stats.GetTotalVertexCount());
    ImGui::Text("Indices : %d", stats.GetTotalIndexCount());

    ImGui::ColorEdit4("Square Color", glm::value_ptr(m_SquareColor));
    ImGui::End();
}

代码运行正常的话,会看到文章开头的画面。接下来我们简单测试下性能

性能测试

修改MaxQuads值观察是否卡顿

  1. MaxQuads默认是10000,很流畅
static const uint32_t MaxQuads = 10000;

游戏引擎从零开始(38)-Batch Rendering(4)

  1. MaxQuads改成1,有明显的卡顿
static const uint32_t MaxQuads = 1;

游戏引擎从零开始(38)-Batch Rendering(4)

可见批渲染确实能优化性能。这里我们只是粗略的测试和统计。有兴趣的读者可以实现帧率的实时显示,增加滑动条动态调整网格密度以观察帧率的变化。

完整代码&总结

完整代码

github.com/summer-go/H…

总结

批处理在实际应用中是很常见的优化,作为基础属性,在常见的游戏引擎中都有很好的支持。

原文链接:https://juejin.cn/post/7322669567331369023 作者:sumsmile

(0)
上一篇 2024年1月12日 上午11:07
下一篇 2024年1月12日 下午4:01

相关推荐

发表回复

登录后才能评论