游戏引擎从零开始(15)-Buffer API抽象

Application类中还有一些和OpenGL平台相关的代码,需要提出去,使整个引擎逐步向跨平台方向演进。

本节将VertexBuffer和IndexBuffer抽象出来。设计成抽象接口Buffer,具体实现下沉到platform包中。

C++中实现抽象(多态)有两种形式,编译时多态和运行时,各有优缺点。

  • 编译时多态,可以通过模板、宏定义实现。也叫静态绑定,运行时性能相对好一些
  • 运行时多态,通过virtual函数实现。代码逻辑更符合面向对象,好理解,且修改平台重新编译的代码较少。需要运行时动态替换虚函数表,性能有一定下降。

本章节,我们基于虚函数实现渲染平台的动态切换,原教程作者提出,基于自己多年的经验,基于虚函数的多态,性能的损耗很小。

Buffer API抽象设计

设计三个类:

  • 基类Buffer
  • 实现类OpenGLBuffer
  • 工具类Renderer

Renderer中控制具体运行时用哪个平台的API,是OpenGL的呢,还是windows平台的DX。暂时我们只实现OpenGL平台的API。

工具类Renderer

暂时仅有一个获取平台类别的接口

Sandbox/Hazel/src/Hazel/Renderer/Renderer.h

c++ 复制代码
#pragma once

namespace Hazel {
    enum class RendererAPI {
        None = 0, OpenGL = 1
    };

    class Renderer {
    public:
        inline static RendererAPI GetAPI() {
            return s_RendererAPI;
        }
    private:
        static RendererAPI s_RendererAPI;
    };
}

Sandbox/Hazel/src/Hazel/Renderer/Renderer.cpp

c++ 复制代码
#include "Renderer.h"

namespace Hazel{
    RendererAPI Renderer::s_RendererAPI = RendererAPI::OpenGL;
}

基类Buffer

设计成员函数Bind、Unbind,及类静态函数Create。实际调用OpenGL的drawElements时,需要知道index的数量,所以IndexBuffer还需要GetCount接口。

Sandbox/Hazel/src/Hazel/Renderer/Buffer.h

c++ 复制代码
#pragma once
#include <stdint.h>
namespace Hazel {
    class VertexBuffer {
    public:
        virtual ~VertexBuffer(){}

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;

        static VertexBuffer* Create(float* vertices, uint32_t size);
    };

    class IndexBuffer {
    public:
        virtual ~IndexBuffer(){}

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;
        virtual uint32_t GetCount() const = 0;
        static IndexBuffer* Create(uint32_t* indices, uint32_t size);
    };
}

Sandbox/Hazel/src/Hazel/Renderer/Buffer.cpp

c++ 复制代码
//
// Created by xiatian05 on 2023/9/30.
//

#include "Buffer.h"
#include "Renderer.h"
#include "Platform/OpenGL/OpenGLBuffer.h"
#include "Log.h"
#include "Base.h"

namespace Hazel {
    VertexBuffer *VertexBuffer::Create(float *vertices, uint32_t size) {
        switch (Renderer::GetAPI()) {
            case RendererAPI::None: HZ_CORE_ASSERT(false, "RendererAPI::None is currently not supported!")
                return nullptr;
            case RendererAPI::OpenGL:
                return new OpenGLVertexBuffer(vertices, size);

        }

        HZ_CORE_ASSERT(false, "Unknow RendererAPI!");
        return nullptr;
    }

    IndexBuffer *IndexBuffer::Create(uint32_t *indices, uint32_t size) {
        switch (Renderer::GetAPI()) {
            case RendererAPI::None: HZ_CORE_ASSERT(false, "RendererAPI::None is currently not supported!")
                return nullptr;
            case RendererAPI::OpenGL:
                return new OpenGLIndexBuffer(indices, size);

        }

        HZ_CORE_ASSERT(false, "Unknow RendererAPI!");
        return nullptr;
    }
}

实现类OpenGLBuffer

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLBuffer.h

c++ 复制代码
#pragma once
#include <stdint.h>
#include "Renderer/Buffer.h"

namespace Hazel {
    class OpenGLVertexBuffer : public VertexBuffer {
    public:
        OpenGLVertexBuffer(float* vertices, uint32_t size);
        virtual ~OpenGLVertexBuffer();

        void Bind() const override;
        void Unbind() const override;

    private:
        uint32_t m_RendererID;
    };

    class OpenGLIndexBuffer : public IndexBuffer {
    public:
        OpenGLIndexBuffer(uint32_t* indices, uint32_t count);

        virtual ~OpenGLIndexBuffer();

        void Bind() const override;

        void Unbind() const override;

        uint32_t GetCount() const override {return m_Count;}

    private:
        uint32_t m_RendererID;
        uint32_t m_Count;
    };
}

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLBuffer.cpp

c++ 复制代码
//
// Created by xiatian05 on 2023/9/30.
//

#include "OpenGLBuffer.h"
#include <glad/glad.h>

namespace Hazel {


    /////////////////////////////////////////////////////////////////////////////
    // VertexBuffer /////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////

    OpenGLVertexBuffer::OpenGLVertexBuffer(float *vertices, uint32_t size) {
        glGenBuffers(1, &m_RendererID);
        glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
        glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
    }

    OpenGLVertexBuffer::~OpenGLVertexBuffer() {
        glDeleteBuffers(1, &m_RendererID);
    }

    void OpenGLVertexBuffer::Bind() const {
        glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
    }

    void OpenGLVertexBuffer::Unbind() const {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    /////////////////////////////////////////////////////////////////////////////
    // IndexBuffer /////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////

    OpenGLIndexBuffer::OpenGLIndexBuffer(uint32_t *indices, uint32_t count):m_Count(count) {
        glGenBuffers(1, &m_RendererID);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
    }

    OpenGLIndexBuffer::~OpenGLIndexBuffer() {
        glDeleteBuffers(1, &m_RendererID);
    }

    void OpenGLIndexBuffer::Bind() const {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
    }

    void OpenGLIndexBuffer::Unbind() const {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }
}

使用抽象接口替换Application中的GL逻辑

头文件中增加vertexBuffer、IndexBuffer,当然了,用智能指针包装。

Sandbox/Hazel/src/Hazel/Application.h

c++ 复制代码
#include "LayerStack.h"
#include "ImGui/ImGuiLayer.h"
#include "Renderer/Shader.h"
#include "Renderer/Buffer.h"

namespace Hazel{
class Application {
	@@ -36,8 +37,10 @@ class Application {
    ImGuiLayer* m_ImGuiLayer;
    bool m_Running = true;
    LayerStack m_LayerStack;
    unsigned int m_VertexArray;
    std::unique_ptr<Shader> m_Shader;
    std::unique_ptr<VertexBuffer> m_VertexBuffer;
    std::unique_ptr<IndexBuffer> m_IndexBuffer;
private:
    static Application* s_Instance;
};

Application.cpp中替换掉对vertexBuffer和IndexBuffer的操作,使用抽象基类的接口。 Sandbox/Hazel/src/Hazel/Application.cpp

完整代码参考 & 总结

本次代码修改commit:Buffer API abstract

本章,将vertexBuffer和IndexBuffer提炼出来,抽象成与平台无关的接口,进一步减少了上层API对OpenGL的直接依赖,当然了,还有一些接口,后续逐步会替换掉。

代码中有一些编码上的设计,值得我们学习,比如Create接口,就是一种门面设计,内聚到Buffer类中,对使用方更友好。

相关推荐
老歌老听老掉牙3 分钟前
Open CASCADE学习|几何体切片处理:OpenMP与OSD_Parallel并行方案深度解析
c++·学习·open cascade·切片处理
superior tigre40 分钟前
C++学习:六个月从基础到就业——C++20:范围(Ranges)基础
c++·学习·c++20
虾球xz2 小时前
游戏引擎学习第294天:增加手套
c++·学习·游戏引擎
龙湾开发2 小时前
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 13.几何着色器(二)爆炸效果&修改图元类型
c++·笔记·学习·3d·图形渲染·着色器
ALex_zry2 小时前
C++ Kafka客户端(cppkafka)安装与问题解决指南
开发语言·c++·kafka
黑牛先生2 小时前
C++ 回调函数
开发语言·c++
laimaxgg3 小时前
高并发内存池|二、Common
c++·性能优化
June`3 小时前
专题五:floodfill算法(太平洋大西洋水流问题)
c++·算法·leetcode·深度优先·剪枝
yanjiee4 小时前
Cursor无法使用C/C++调试的解决办法
c语言·开发语言·c++·vscode
爱吃涮毛肚的肥肥(暂时吃不了版)4 小时前
仿腾讯会议——添加音频
c++·算法·面试·职场和发展·音视频·腾讯会议