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

总结:

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

相关推荐
W23035765732 小时前
经典算法:最长上升子序列(LIS)深度解析 C++ 实现
开发语言·c++·算法
.Ashy.2 小时前
2026.4.11 蓝桥杯软件类C/C++ G组山东省赛 小记
c语言·c++·蓝桥杯
minji...3 小时前
Linux 线程同步与互斥(三) 生产者消费者模型,基于阻塞队列的生产者消费者模型的代码实现
linux·运维·服务器·开发语言·网络·c++·算法
CoderCodingNo5 小时前
【GESP】C++三级真题 luogu-B4499, [GESP202603 三级] 二进制回文串
数据结构·c++·算法
hetao17338376 小时前
2026-04-09~12 hetao1733837 的刷题记录
c++·算法
6Hzlia6 小时前
【Hot 100 刷题计划】 LeetCode 136. 只出现一次的数字 | C++ 哈希表&异或基础解法
c++·算法·leetcode
汉克老师7 小时前
GESP2024年6月认证C++三级( 第二部分判断题(1-10))
c++·数组·位运算·补码·gesp三级·gesp3级
无限进步_8 小时前
【C++】只出现一次的数字 II:位运算的三种解法深度解析
数据结构·c++·ide·windows·git·算法·leetcode
小贾要学习8 小时前
【Linux】TCP网络通信编程
linux·服务器·网络·c++·网络协议·tcp/ip
哎嗨人生公众号9 小时前
手写求导公式,让轨迹优化性能飞升,150ms变成9ms
开发语言·c++·算法·机器人·自动驾驶