游戏引擎从零开始(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...

总结:

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

相关推荐
clint45619 小时前
C++进阶(1)——前景提要
c++
甲维斯1 天前
Fable+Codex 《坦克大战3D》双端发布了!
人工智能·ai编程·游戏开发
夜悊1 天前
C++代码示例:进制数简单生成工具
c++
SmalBox1 天前
【节点】[Houndstooth节点]原理解析与实际应用
unity3d·游戏开发·图形学
郝学胜_神的一滴1 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0012 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
玖玥拾2 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
один but you2 天前
constexpr函数
c++
凡人叶枫2 天前
Effective C++ 条款41:了解隐式接口和编译期多态
java·开发语言·c++·effective c++
凡人叶枫2 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++