游戏引擎从零开始(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 小时前
【优秀三方库研读】【性能优化点滴】odygrd/quill 解决伪共享
c++·性能优化·三方库研读
YuforiaCode2 小时前
第十二届蓝桥杯 2021 C/C++组 空间
c语言·c++·蓝桥杯
YuforiaCode2 小时前
第十二届蓝桥杯 2021 C/C++组 卡片
c语言·c++·蓝桥杯
努力努力再努力wz4 小时前
【Linux实践系列】:进程间通信:万字详解命名管道实现通信
android·linux·运维·服务器·c++·c
炯哈哈5 小时前
【上位机——MFC】文档
开发语言·c++·mfc·上位机
愚润求学5 小时前
【C++11】可变参数模板
开发语言·c++·笔记·c++11·模板
WW_千谷山4_sch5 小时前
MYOJ_1349:(洛谷P3951)[NOIP 2017 提高组] 小凯的疑惑(数学公式套用,两步搞定代码)
c++·算法
Thomas游戏开发5 小时前
Unity3D Timeline扩展与自定义事件处理
前端框架·unity3d·游戏开发
共享家95276 小时前
深入探究C++ 中的stack、queue和deque
c++
How_doyou_do6 小时前
项目实战-贪吃蛇大作战【补档】
c语言·c++·visual studio