首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >高效的C++资源管理器

高效的C++资源管理器
EN

Code Review用户
提问于 2015-11-01 16:27:40
回答 1查看 1.2K关注 0票数 3

这是我最后一次尝试创建一个高效的ResourceManager类,它负责分配OpenGL对象(比如阴影、纹理、网格、.)。它将每个资源存储在一个unique_ptr中,然后分发这种类型的const指针,我称之为“观察指针”。它们不能被删除,因为资源分配只能通过ResourceManager类完成。

ResourceManager.h

代码语言:javascript
复制
#pragma once
#include <memory>
#include <vector>
#include <unordered_map>
#include "../render/Shader.h"
#include "../render/Mesh.h"
#include "../render/Texture.h"

namespace Spiky
{

class ResourceManager
{
    public:

        using ShaderRepo = std::unordered_map<const char*, std::unique_ptr<CShader>>;
        using MeshRepo = std::unordered_map<const char*, std::unique_ptr<CMesh>>;
        using TextureRepo = std::unordered_map<const char*, std::unique_ptr<CTexture>>;

        //Shader
        static const CShader* LoadShader(const char* ID, const char* vs, const char* fs)
        {
            shaderObjects.insert(std::pair<const char*, std::unique_ptr<CShader>>(ID, std::make_unique<CShader>(
                                                                                      (shaderRootDir + std::string(vs)).c_str(),
                                                                                      (shaderRootDir + std::string(fs)).c_str())));
            return (shaderObjects.at(ID).get());
        }

        static const CShader* LoadShader(const char* ID, const char* vs, const char* fs, const char* gs)
        {
            shaderObjects.insert(std::pair<const char*, std::unique_ptr<CShader>>(ID, std::make_unique<CShader>(
                                                                                      (shaderRootDir + std::string(vs)).c_str(),
                                                                                      (shaderRootDir + std::string(fs)).c_str(),
                                                                                      (shaderRootDir + std::string(gs)).c_str())));
            return (shaderObjects.at(ID).get());
        }

        static CShader* GetShader(const char* ID)
        {
            return (shaderObjects.at(ID).get());
        }

        //Mesh
        static const CMesh* LoadMesh(const char* ID, Vertex* vertices, unsigned int numVertices, unsigned int* indeces, unsigned int numIndices)
        {
            meshObjects.insert(std::pair<const char*, std::unique_ptr<CMesh>>(ID, std::make_unique<CMesh>(
                                                                                  vertices,
                                                                                  numVertices,
                                                                                  indeces,
                                                                                  numIndices)));
            return (meshObjects.at(ID).get());
        }

        static const CMesh* LoadMesh(const char* ID, const char* fileName)
        {
            meshObjects.insert(std::pair<const char*, std::unique_ptr<CMesh>>(ID, std::make_unique<CMesh>(
                                                                                  (meshRootDir + std::string(fileName)).c_str())));
            return (meshObjects.at(ID).get());
        }

        //Texture
        static const CTexture* LoadTexture(const char* ID, const char* texturePath, GLenum texTarget = GL_TEXTURE_2D, GLfloat filter = GL_LINEAR,
            GLfloat pattern = GL_REPEAT, GLenum attachment = GL_NONE)
        {
            textureObjects.insert(std::pair<const char*, std::unique_ptr<CTexture>>(ID, std::unique_ptr<CTexture>(new CTexture(
                (textureRootDir + std::string(texturePath)).c_str(), texTarget, filter, pattern, attachment))));
            return (textureObjects.at(ID).get());
        }

        static const CTexture* LoadTexture(const char* ID, int width, int height, unsigned char* data = nullptr, GLenum texTarget = GL_TEXTURE_2D,
            GLfloat filter = GL_LINEAR, GLfloat pattern = GL_REPEAT, GLenum attachment = GL_NONE)
        {
            textureObjects.insert(std::pair<const char*, std::unique_ptr<CTexture>>(ID, std::unique_ptr<CTexture>(new CTexture(
                width, height, data, texTarget, filter, pattern, attachment))));
            return (textureObjects.at(ID).get());
        }

        static const CTexture* LoadTextureCustomPath(const char* ID, const char* texturePath, GLenum texTarget = GL_TEXTURE_2D, GLfloat filter = GL_LINEAR, GLfloat pattern = GL_REPEAT, GLenum attachment = GL_NONE)
        {
            textureObjects.insert(std::pair<const char*, std::unique_ptr<CTexture>>(ID, std::unique_ptr<CTexture>(new CTexture(
                texturePath, texTarget, filter, pattern, attachment))));
            return (textureObjects.at(ID).get());
        }

        static const* CTexture GetTexture(const char* ID)
        {
            return (textureObjects.at(ID).get());
        }



    private:
        static ShaderRepo shaderObjects;
        static MeshRepo meshObjects;
        static TextureRepo textureObjects;
        static std::string shaderRootDir;
        static std::string meshRootDir;
        static std::string textureRootDir;
    };
}

ResourceManager.cpp

代码语言:javascript
复制
#include "../core/ResourceManager.h"

namespace Spiky
{
    ResourceManager::ShaderRepo ResourceManager::shaderObjects = ShaderRepo();
    ResourceManager::MeshRepo ResourceManager::meshObjects = MeshRepo();
    ResourceManager::TextureRepo ResourceManager::textureObjects = TextureRepo();
    std::string ResourceManager::shaderRootDir = std::string("assets/shaders/");
    std::string ResourceManager::meshRootDir = std::string("assets/models/");
    std::string ResourceManager::textureRootDir = std::string("assets/images/");
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-11-01 20:39:05

  1. 好吧,您选择的支持类型为您的集合的着色器,网格和纹理有一些有趣的结果:您的键是指针,而不是字符串,这些指针是比较/散列。虽然编译器可以自由地合并字符串文本,但它需要not. --每个自重的编译器在实践中都是在一个翻译单元内这样做,但在这之外,它很快就变得不那么常见了。根据您的目标,有两种解决方法:
    • 将键类型更改为std::string。这是最简单和最通用的解决方案,但它的缺点是潜在的额外分配。
    • 更改集合使用的哈希和比较算法,将指针视为它们指向的字符串值。缺点是,在管理器释放条目之前,必须确保字符串不会更改。

  2. 看看Load@,虽然这些函数都很短,但是有大量的东西需要优化:
    • 需要的时候会有std::make_pair(...)
    • 可以直接连接c样式字符串和std::string,而不需要额外的临时对象。
    • 您可以使用std::emplace来避免不必要地创建std::pair
    • insertemplace已经返回一个迭代器到插入的元素,或者任何阻止插入的东西,并返回一个bool来指示发生了什么。
    • 如果您考虑到元素已经在收集中,重新创建它只是为了摧毁它是一个巨大的浪费。

template静态const CShader* LoadShader(T& ID,const char* vs,const char* fs) { auto res = shaderObjects.emplace(std::forward(ID));if(res.second)尝试{ res.first->second =std::make_unique (shaderRootDir + vs).c_str(),(shaderRootDir+fs).c_str();}catch(.){ shaderObjects.erase(res.first);抛出;}返回res.first->medid.get();}

顺便说一句,我不知道你为什么要用打字机.

据我所见,它们是无用的。

还有,你知道你可以用struct吗?

如果您有一个C++17标准库(因此可以访问try_emplace(...)),那么事情就会变得更加高效:

代码语言:javascript
复制
 return &shaderObjects.try_emplace(ID, (shaderRootDir + vs).c_str(),
     (shaderRootDir + fs).c_str()).first->second;

如果没有,但可以安排CMeshCTextureCShader有效地默认-可构造且可交换或可移动-可分配,这也就足够了。

在紧要关头,如果默认构造函数是轻量级且不能抛出的,那么甚至可以使用显式析构函数调用和放置-new。这不太好,但它足够有效,而且很有效:

代码语言:javascript
复制
template<class T>
static const CShader* LoadShader(T&& ID, const char* vs, const char* fs) {
    auto res = shaderObjects.emplace(std::forward<T>(ID));
    if(res.second)
        try {
            res.first->second.~CShader(); // noexcept
            new((void*)&res.first->second) CShader((shaderRootDir + vs).c_str(),
                (shaderRootDir + fs).c_str());
        } catch(...) {
            new((void*)&res.first->second) CShader(); // efficient and noexcept
            shaderObjects.erase(res.first);
            throw;
        }
    return &res.first->second;
}

在这种情况下,它们应该直接在映射中,因为std::unordered_map中的元素在被擦除之前都将保持在正确的位置,这意味着我们的动态分配少了一个。

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/109459

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档