游戏引擎从零开始(26)-shader缓存

前言

这次要做的工作是,将shader缓存起来,我们设计一个ShaderLibrary缓存Shader,方便在整个引擎范围内对shader进行管理,底层我们用unorder_map<string, int>容器实现存储。

shader加载&缓存

shader中增加GetName方法,用name唯一标识shader,避免重复。Shader::Create方法中,也增加name参数,同时也要修改OpenGLShader中的Create实现。

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

c++ 复制代码
class Shader
{
public:
  virtual ~Shader() = default;
  virtual void Bind() const = 0;
  virtual void Unbind() const = 0;

  virtual const std::string& GetName() const = 0;

  static Ref<Shader> Create(const std::string& filepath);
  static Ref<Shader> Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
};

增加ShaderLibrary类,声明Add、Load、Get、Exists四种接口,对shader进行操作。

c++ 复制代码
class ShaderLibrary {
public:
    void Add(const std::string& name, const Ref<Shader>& shader);
    void Add(const Ref<Shader>& shader);
    Ref<Shader> Load(const std::string& filePath);

    Ref<Shader> Load(const std::string& name, const std::string& filePath);
    Ref<Shader> Get(const std::string& name) const;
    bool Exists(const std::string& name) const;
private:
    std::unordered_map<std::string, Ref<Shader>> m_Shaders;
};

注意,每次Load一个Shader,会缓存到shader map中。

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

c++ 复制代码
void ShaderLibrary::Add(const std::string &name, const Ref<Shader> &shader) {
    HZ_CORE_ASSERT(!Exists(name), "Shader already exists!");
    m_Shaders[name] = shader;
}

void ShaderLibrary::Add(const Ref<Shader> &shader) {
    auto& name = shader->GetName();
    HZ_CORE_ASSERT(!Exists(name), "Shader already exists!");
    Add(name, shader);
}

Ref<Shader> ShaderLibrary::Load(const std::string &filePath) {
    auto shader = Shader::Create(filePath);
    Add(shader);
    return shader;
}

Ref<Shader> ShaderLibrary::Load(const std::string &name, const std::string &filePath) {
    auto shader = Shader::Create(filePath);
    Add(shader);
    return shader;
}

Ref<Shader> ShaderLibrary::Get(const std::string &name) const {
    HZ_CORE_ASSERT(Exists(name), "Share not found!");
    return m_Shaders.at(name);
}

bool ShaderLibrary::Exists(const std::string &name) const {
    return m_Shaders.find(name) != m_Shaders.end();
}

OpenGLShader中增加增加Name

OpenGLShader中增加std::string m_Name。

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

c++ 复制代码
OpenGLShader(const std::string name, const std::string& vertexSrc, const std::string& fragmentSrc);
c++ 复制代码
private:
  std::string m_Name;

将.glsl文件名作为Shader的名称。

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

c++ 复制代码
OpenGLShader::OpenGLShader(const std::string &filePath) {
    std::string source = ReadFile(filePath);
    auto ShaderSources = PreProcess(source);
    Compile(ShaderSources);

    // 以文件名作为shader名字
    // Extract name from filepath
    auto lastSlash = filePath.find_last_of("/\\");
    lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1;
    auto lastDot = filePath.rfind('.');
    auto count = lastDot == std::string::npos ? filePath.size() - lastSlash : lastDot - lastSlash;
    m_Name = filePath.substr(lastSlash, count);
}

Compile中用array替换vector

OpenGLShader::Compile方法中,有一处代码可以优化。glShaderIDs用std::array代替std::vector。

std::vector方便拓展,用起来很方便,但是内容存储于堆上,相对而言,std::array内存在栈上申请,性能会更好一些。

且shader的引用是一个int值,不会占太多内存。

c++ 复制代码
void OpenGLShader::Compile(const std::unordered_map<GLenum, std::string> &shaderSources) {
    GLuint program = glCreateProgram();
    HZ_CORE_ASSERT(shaderSources.size() <= 2, "we only support 2 shaders for now");
    std::array<GLenum, 2> glShaderIDs;
    int glShaderIDIndex = 0;
    ...
    ...
    glAttachShader(program, shader);
    glShaderIDs[glShaderIDIndex++] = shader;
}

SandBoxApp中使用ShaderLibrary

将textureShader的直接调用,改为通过ShaderLibrary.Load()方法

c++ 复制代码
auto textureShader = m_ShaderLibrary.Load("../assets/shaders/Texture.glsl");
...
textureShader->Bind();

还有一些细节不一一说明了,请参考下面完整代码对比参考。

完整代码&总结

修改的地方比较零碎,参考完整代码:
github.com/summer-go/H...

总结:

所有的软件工程,做到最后都是在扣性能,需要开发者对内存很敏感,对容器的区别很熟悉,才能针对自己的业务场景做深度优化。

相关推荐
苏克贝塔1 分钟前
Qt 图形视图框架3-事件处理与传播
c++·qt
轩情吖7 分钟前
Qt的信号与槽(二)
数据库·c++·qt·信号·connect·信号槽·
胖大和尚9 分钟前
C++项目学习计划
开发语言·c++·学习
GiraKoo2 小时前
【GiraKoo】 C++20的新特性
c++
无聊的小坏坏2 小时前
力扣 239 题:滑动窗口最大值的两种高效解法
c++·算法·leetcode
黎明smaly2 小时前
【排序】插入排序
c语言·开发语言·数据结构·c++·算法·排序算法
CCF_NOI.3 小时前
(普及−)B3629 吃冰棍——二分/模拟
数据结构·c++·算法
Thomas游戏开发4 小时前
Unity3D Boehm GC原理解析
前端框架·unity3d·游戏开发
眠りたいです4 小时前
Mysql常用内置函数,复合查询及内外连接
linux·数据库·c++·mysql
笑鸿的学习笔记5 小时前
qt-C++语法笔记之Stretch与Spacer的关系分析
c++·笔记·qt