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

总结:

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

相关推荐
ajassi20005 小时前
开源 C++ QT Widget 开发(七)线程--多线程及通讯
linux·c++·qt·开源
mit6.8246 小时前
8.27 网格memo
c++·算法
jeffery8926 小时前
4056:【GESP2403八级】接竹竿
数据结构·c++·算法
Forward♞6 小时前
Qt——界面美化 QSS
开发语言·c++·qt
快乐的划水a8 小时前
解释器模式及优化
c++·设计模式·解释器模式
岁忧8 小时前
(LeetCode 每日一题) 498. 对角线遍历 (矩阵、模拟)
java·c++·算法·leetcode·矩阵·go
kyle~9 小时前
C/C++---前缀和(Prefix Sum)
c语言·c++·算法
YxVoyager11 小时前
【C++标准库】<ios>详解基于流的 I/O
c语言·c++
tan77º14 小时前
【项目】分布式Json-RPC框架 - 抽象层与具象层实现
linux·服务器·c++·分布式·tcp/ip·rpc·json
zzx_blog14 小时前
c++函数工厂实现两种方式:lambda和function
c++