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类中,对使用方更友好。