在c ++和opengl中努力进行内存管理

时间:2018-12-12 23:27:35

标签: c++ opengl memory-management memory-leaks

我正在为一个c ++项目进行内存管理。 (这只是一个有趣的项目,需要学习) 目前,它基本上是一个使用glfw,opengl,glm和assimp构建的3d文件查看器。 我能够成功加载一些网格文件,并在带有着色器的窗口中显示它们,甚至显示纹理!哇
我什至可以显示多个对象,但这并不是一件容易的事。

“代码库”仍然很小,但是足够我犯很多错误。

因此,基本上它可以按预期工作,但事实是,在加载新的网格文件时,该程序永远不会真正释放任何内存。 它有时会下降1或2 Mb,但除此之外,内存占用量还在不断增长(尽管仅当我加载对象时,当然在程序执行期间并不连续)

我已经尝试了几乎所有可以想到的方法来管理内存。但是我所做的任何事情都没有任何效果。 我尝试限制“新”的使用,因为据我所知,管理起来有点棘手(即需要手动删除) 当我不再需要用clear()函数清除std :: vectors时,我也尝试过一切

我将把main.cpp放在这里 也许问题就在那里。 所有源文件都可以在https://github.com/gui2one/angine

中找到
#include <iostream>
#include "application.h"
#include "pch.h"

#include "generators/gridmesh.h"
#include "generators/spheremesh.h"

Object* obj1 = new Object();

int nCols = 4;
Application app ;

Mesh loadNewObject(){
    char file[1024];
    FILE *f = popen("zenity --file-selection --title='choose a 3d file' ", "r");
    fgets(file, 1024, f);   
    std::cout << "Loading -> "<<file << "\n";           
    ObjLoader loader;
    Mesh mesh;
    std::string str(file);

    str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());

    mesh = loader.assimp_load(str);

    return mesh;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    std::cout << "key ->" << key << ", scancode --> "<< scancode << "\n";
    if (key == GLFW_KEY_UP && action == GLFW_PRESS){

        nCols +=3;

        obj1->mesh.clearAll();

        SphereMesh* sphere = new SphereMesh();
        sphere->generate(obj1->mesh,20,nCols);

        delete sphere;

        std::cout << nCols << "\n";
        obj1->buildVbo();

    }else if (key == GLFW_KEY_DOWN && action == GLFW_PRESS){
        if( nCols > 4){
            nCols -=3;
            obj1->mesh.clearAll();

            SphereMesh* sphere = new SphereMesh();
            sphere->generate(obj1->mesh,20,nCols);

            delete sphere;
            std::cout << nCols << "\n";
            obj1->buildVbo();

        }
    }else if (key == 79 /* letter o*/ && action == GLFW_PRESS){

        nCols -=3;
        obj1->mesh.clearAll();
        obj1->mesh = loadNewObject();
        std::cout << nCols << "\n";
        obj1->buildVbo();
    }
}

int main(){



    std::cout << "angine PROJECT\n";

    //obj1->mesh = loadNewObject();

    SphereMesh* sphere = new SphereMesh();
    sphere->generate(obj1->mesh,20,nCols);        
    delete sphere;

    obj1->color->x = 0.9;
    obj1->color->y = 0.8;
    obj1->color->z = 0.5;
    obj1->color->w = 1.0;

    obj1->position->x = 1.2f;

    obj1->shader.loadVertexShaderSource("../src/res/shaders/basic_shader.vert");
    obj1->shader.loadFragmentShaderSource("../src/res/shaders/basic_shader.frag");  

    obj1->buildVbo();
    obj1->buildTexture();
    obj1->shader.createShader();


    app.objects.push_back(obj1);




    app.window.objects = app.objects;

    glfwSetKeyCallback(app.window.win, key_callback);
    while(!app.window.shouldClose()){



        app.window.refresh();
        obj1->rotation->x = glfwGetTime()*0.2;
        obj1->rotation->y = glfwGetTime()*0.13;
        obj1->rotation->z = glfwGetTime()*0.11;


    }

    return 0;
}

负责加载网格数据ObjLoader的类:

ObjLoader.h:

#ifndef OBJLOADER_H
#define OBJLOADER_H
#include <string>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <stdio.h>

#include "../mesh.h"
#include "../vector3.h"

#include "../include/assimp/cimport.h"
#include "../include/assimp/scene.h"
#include "../include/assimp/postprocess.h"

class ObjLoader{
    public:
        Mesh assimp_load(std::string file_path);

};

#endif

ObjLoader.cpp:

Mesh ObjLoader::assimp_load(std::string file_path){

    const struct aiScene* scene = NULL;
    scene = aiImportFile(file_path.c_str(), aiProcessPreset_TargetRealtime_MaxQuality);

    if(scene){


        std::vector<Vertex> vertices;
        std::vector<float> positions;
        std::vector<float> normals;
        for (int i = 0; i < scene->mMeshes[0]->mNumFaces; i++)
        {
            Vertex vertex;

            for (int j = 0; j < scene->mMeshes[0]->mFaces[i].mNumIndices; j++)
            {
                    int index = scene->mMeshes[0]->mFaces[i].mIndices[j];

                    vertex.position.x = scene->mMeshes[0]->mVertices[index].x;
                    vertex.position.y = scene->mMeshes[0]->mVertices[index].y;
                    vertex.position.z = scene->mMeshes[0]->mVertices[index].z;

                    vertex.normal.x = scene->mMeshes[0]->mNormals[index].x;
                    vertex.normal.y = scene->mMeshes[0]->mNormals[index].y;
                    vertex.normal.z = scene->mMeshes[0]->mNormals[index].z;



                    if(scene->mMeshes[0]->HasTextureCoords(0)){

                        //~ std::cout << scene->mMeshes[0]->mTextureCoords[0][index].x  << "-------------"<< "\n";
                        vertex.t_coords.x = scene->mMeshes[0]->mTextureCoords[0][index].x;
                        vertex.t_coords.y = scene->mMeshes[0]->mTextureCoords[0][index].y;
                    }


                    vertices.push_back(vertex);

                    positions.push_back(scene->mMeshes[0]->mVertices[index].x);
                    positions.push_back(scene->mMeshes[0]->mVertices[index].y);
                    positions.push_back(scene->mMeshes[0]->mVertices[index].z);
            }

        }
        Mesh mesh;

        mesh.vertices = vertices;

        //// trying to delete everything I can
        delete scene;
        vertices.clear();
        positions.clear();
        normals.clear();

        return mesh;
    }else{
        Mesh empty_mesh;
        return empty_mesh;
    }


}

事实是,即使不使用此类加载网格,而是通过程序生成网格,问题仍然存在。 我不能真正在这里发布所有内容,但是我也有一些opengl代码来绘制这些网格。在这里,我还尝试“清除”所有内容,并在使用它们后取消绑定gl缓冲区。

我没主意了。 我知道这个问题很模糊。当我进行更多调查时,我将尝试使其更加精确,但是我确实需要一些帮助来理解。

2 个答案:

答案 0 :(得分:0)

仅通过阅读源代码而不坐在那里并逐步调试程序来手动查看对象的所有动态内存分配和释放是很难分辨的。这是一项非常繁琐的任务。给出决定性的答案可能非常困难。我可以在这里做的一件事是为您提供一些建议,因为我过去已经构建了3D图形引擎。

您已经提到了消除或最小化使用newdelete的事实,这是一个很好的开始。我要提出的是一种设计模式,主要是结构的伪代码。您在这里可以做的是创建一个类,该类负责将管理您的内存的所有3D引擎资产。这不是一件容易的事,但是一旦框架就位并正确工作,它将有助于简化代码库和代码管理。

您可以创建一个将存储所有资产的类。但是,这样做您将需要用于不同对象的通用结构或类。它可能看起来像这样:

class AssetStorageManager {
private:
    std::vector<std::shared_ptr<Texture>> textures_;
    std::vector<std::shared_ptr<Font>>    fonts_;
    std::vector<std::shared_ptr<Model>>   models_;
    std::vector<std::shard_ptr<GUI>>      guiObjects_; // Text Boxes, Buttons etc.
    // other possible containers: Music(BGM), SoundEffects etc.
    // shaders (Vertex, Fragment, Geometry & OpenGL Programs)...
public:
    AssetStorageManager() = default;
    ~AssetStorageManager() = default;

    void addTexture( const Texture& texture ) { /* ... ; */ }
    void removeTexture( unsigned int textureID ) { /* ... ; */ }
    void clearTextures() { /* remove all textures in container; */ }
    Texture* getTexture( unsigned int texureID );

    // basically the same 4 for all of your individual containers.
};

有时vector可能不足,您可能需要mapunordered mapmultimaplistqueue(优先)等。这完全取决于您需要哪个容器。

此类将负责管理内存。需要考虑的另一件事是:当应用程序运行时,您将仅需要此类对象的单个实例,这是Singleton类型的对象可用的地方。如果将此类设为Singleton对象,则在从文件加载任何类型的对象以进行存储之前,都需要先创建和初始化该类。


在我的引擎中,这些是我拥有的单例类型,但是它们是从Singleton基类继承的:

-Singleton.h-

namespace /* namespace name here*/ {

#ifndef SINGELTON_H
#define SINGLETON_H

class Singleton {
public:
    enum SingletonType {
        LOGGER = 0, // Must be first!
        SETTINGS,
        ENGINE,
        ANIMATION_MANAGER,
        SHADER_MANGER,
        ASSET_STORAGE,
        AUDIO_MANAGER,
        FONT_MANAGER,
        BATCH_MANAGER
    };
private:
    SingletonType type_;
public:
     virtual ~Singleton();
protected:
     explicit Singleton( SingletonType eType );

     void logMemoryAllocation( bool isAllocated ) const;
private:
     Singleton( const Singleton& c ); // not implemented
     Singleton& operator=( const Singleton& c ); // not implemented            
}; 

} // /*namespace name here */

#endif // SINGLETON_H

-Singelton.cpp-

#include "stdafx.h"
#include "Singleton.h"

#include "Logger.h"
#include "Settings.h"

namespace /* namespace name here*/ {

struct SingletonInfo {
    const std::string strSingletonName;
    bool              isConstructed;

    SingletonInfo( const std::string& strSingletonNameIn ) :
        strSingletonName( strSingletonNameIn ),
        isConstructed( false )
    {}
};

// Order Must Match Types Defined In Singleton::SingeltonType enum
static std::array<SingletonInfo, 9> s_aSingletons = { SingletonInfo( "Logger" ),
                                                      SingletonInfo( "Settings" ),
                                                      SingletonInfo( "Engine" ),
                                                      SingletonInfo( "AnimationManager" ),
                                                      SingletonInfo( "ShaderManager" ),
                                                      SingletonInfo( "AssetStorage" ),                                                  
                                                      SingletonInfo( "AudioManager" ), 
                                                      SingletonInfo( "FontManager" ),
                                                      SingletonInfo( "BatchManager" ) };

Singleton::Singleton( SingletonType eType ) :
m_eType( eType ) {
    bool bSaveInLog = s_aSingletons.at( TYPE_LOGGER ).isConstructed;

    try {
        if ( !s_aSingletons.at( eType ).isConstructed ) {
            // Test Initialization Order
            for ( int i = 0; i < eType; ++i ) {
                if ( !s_aSingletons.at( i ).isConstructed ) {
                    throw ExceptionHandler( s_aSingletons.at( i ).strSingletonName + " must be constructued before constructing " + s_aSingletons.at( eType ).strSingletonName, bSaveInLog );
                }
            }
            s_aSingletons.at( eType ).isConstructed = true;

            if ( s_aSingletons.at( TYPE_ENGINE ).isConstructed && 
                 Settings::get()->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
                logMemoryAllocation( true );
            }

        } else {
            throw ExceptionHandler( s_aSingletons.at( eType ).strSingletonName + " can only be constructed once.", bSaveInLog );
        }
    } catch ( std::exception& ) {
        // eType Is Out Of Range
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Invalid Singelton Type sepcified: " << eType;
        throw ExceptionHandler( strStream, bSaveInLog );
    }
}    

Singleton::~Singleton() {
    if ( s_aSingletons.at( TYPE_ENGINE ).isConstructed &&
         Settings::get()->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
        logMemoryAllocation( false );
    }
    s_aSingletons.at( m_eType ).isConstructed = false;
}    

void Singleton::logMemoryAllocation( bool isAllocated ) const {
    if ( isAllocated ) {
        Logger::log( "Created " + s_aSingletons.at( m_eType ).strSingletonName );
    } else {
        Logger::log( "Destroyed " + s_aSingletons.at( m_eType ).strSingletonName );
    }
}

} // /*namespace name here */

这是我的所有Singleton对象的基类,这些对象管理引擎的不同组件。还涉及其他类,例如LoggerSettingsBatchManager等。所有这些都是Singleton本身,我将不在这里显示。这不会在您的计算机上编译。

从中继承的我的AssetStorage类看起来像这样:

-AssetStorage.h -

#ifndef ASSET_STORAGE_H
#define ASSET_STORAGE_H

#include "Singleton.h"
#include "CommonStructs.h"

namespace /*namespace name here*/ {

class BaseMko;
class GuiElement;
enum GuiType;

struct Texture {
    bool        hasAlphaChannel;
    bool        generateMipMap;
    bool        wrapRepeat;
    unsigned    uWidth;
    unsigned    uHeight;
    TextureInfo::FilterQuality filterQuality;
    std::vector<unsigned char> vPixelData;

    Texture( TextureInfo::FilterQuality filterQualityIn, bool generateMipMapIn, bool wrapRepeatIn ) :
        hasAlphaChannel( false ),
        generateMipMap( generateMipMapIn ),
        wrapRepeat( wrapRepeatIn ),
        uWidth( 0 ),
        uHeight( 0 ),
        filterQuality( filterQualityIn )
    {}
}; // Texture

class AssetStorage sealed : public Singleton {
private:
    typedef std::unordered_map<std::string, std::shared_ptr<BaseMko>>       MapMkoAssets;
    typedef std::unordered_map<std::string, TextureInfo>                    MapTextureInfos;
    typedef std::unordered_map<std::string, std::shared_ptr<GuiElement>>    MapGuiAssets;

    MapMkoAssets    m_mkoAssets;
    MapTextureInfos m_textureInfos;
    MapGuiAssets    m_guiScreenAssets;
    MapGuiAssets    m_guiRenderableAssets;

    std::vector<std::string> m_vGuiForRemoval;

public:
    AssetStorage();
    virtual ~AssetStorage();

    static AssetStorage* const get();

    // Mko Objects
    BaseMko*    getMko( const std::string& strId ) const;
    void        add( BaseMko* pMko );
    bool        removeMko( const std::string& strId, bool removeTexture = false );
    void        showLoadedMkoObjects() const;

    // Texture Objects
    TextureInfo getTextureInfo( const std::string& strFilename ) const;
    TextureInfo add( const Texture& texture, const std::string& strFilename );
    bool        removeTextureInfo( const std::string& strFilename );
    bool        removeTextureInfo( unsigned uTextureId );
    void        showLoadedTextureInfo() const;

    // Gui Objects
    GuiElement* getGuiElement( const std::string& strId, GuiType type ) const;
    void        add( GuiElement* pGui, GuiType type );
    bool        removeGuiElement( const std::string& strId, bool removeTextures = false );
    template<typename T>
    bool        removeGuiElement( T* pGui, bool removeTextures = false );
    void        markGuiForRemoval( const std::string& strId );
    void        cleanOrphanedGuiObjects();
    void        showLoadedGuiObjects() const;

private:
    AssetStorage( const AssetStorage& c ) = delete; // Not Implemented
    AssetStorage& operator=( const AssetStorage& c ) = delete; // Not Implemented

    // Gui Objects
    GuiElement* getGuiElement( const std::string& strId, const MapGuiAssets& guiMap ) const;
    void        add( GuiElement* pGui, MapGuiAssets& guiMap );
    bool        removeGuiElement( const std::string& strId, MapGuiAssets& guiMap, bool removeTextures );
}; // AssetStorage

template<typename T>
inline bool AssetStorage::removeGuiElement( T * pGui, bool removeTextures ) {
    return false;
}

} // /*namespace name here*/

#endif // ASSET_STORAGE_H

-AssetStorage.cpp-

#include "stdafx.h"
#include "AssetStorage.h"

#include "BaseMko.h"
#include "BlockThread.h"
#include "Logger.h"
#include "Settings.h"
#include "GuiScreen.h"
#include "GuiRenderable.h"

namespace /*namespace name here*/ {

// Core OpenGL API Found In Opengl32.lib
#ifdef __cplusplus
    extern "C" {
#endif
GLAPI void APIENTRY glBindTexture( GLenum target, GLuint texture );
GLAPI void APIENTRY glDeleteTextures( GLsizei n, const GLuint *textures );
GLAPI void APIENTRY glGenTextures( GLsizei n, GLuint *textures );
GLAPI void APIENTRY glTexImage2D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels );
GLAPI void APIENTRY glTexParameteri( GLenum target, GLenum pname, GLint param );
#ifdef __cplusplus
    }
#endif

// OpenGl Texture API
static PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr;

static Settings*        s_pSettings     = nullptr;
static AssetStorage*    s_pAssetStorage = nullptr;

static CRITICAL_SECTION s_criticalSection;

static void defineOpenglApi() {
    std::ostringstream strStream;
    strStream << __FUNCTION__ << " Can not initialize function pointer named: ";
    std::string strApiName;

    if ( Settings::get()->getOpenglVersion().x >= 3 ) {
        strApiName = "glGenerateMipmap";
        if ( ( glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress( strApiName.c_str() ) ) == nullptr ) {
            strStream << strApiName;
            throw ExceptionHandler( strStream );
        }
    }

}    

AssetStorage::AssetStorage() :
Singleton( TYPE_ASSET_STORAGE ) {
    InitializeCriticalSection( &s_criticalSection );    
    defineOpenglApi();    
    s_pSettings = Settings::get();
    s_pAssetStorage = this;
}

AssetStorage::~AssetStorage() {
    s_pAssetStorage = nullptr;

    m_mkoAssets.clear();
    m_guiRenderableAssets.clear();
    m_guiScreenAssets.clear();

    for( MapTextureInfos::iterator itTexture = m_textureInfos.begin(); itTexture != m_textureInfos.end(); ++itTexture ) {
        if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
            Logger::log( std::string( "Destroyed " ) + itTexture->first );
        }
        glDeleteTextures( 1, &(itTexture->second.uTextureId ) );
    }
    m_textureInfos.clear();

    DeleteCriticalSection( &s_criticalSection );
}    

AssetStorage* const AssetStorage::get() {
    if ( nullptr == s_pAssetStorage ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " failed, AssetStorage has not been constructed yet" ) );
    }
    return s_pAssetStorage;
} 

BaseMko* AssetStorage::getMko( const std::string& strId ) const {
    BlockThread blockThread( s_criticalSection );

    MapMkoAssets::const_iterator itMkoAssets = m_mkoAssets.find( strId );
    if ( itMkoAssets == m_mkoAssets.cend() ) {
        return nullptr;
    }
    return itMkoAssets->second.get();
}

void AssetStorage::add( BaseMko* pMko ) {
    if ( nullptr == pMko ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " pMko==nullptr passed in" ) );
    }
    if ( nullptr != getMko( pMko->getId() ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " attempting to store " << pMko->getId() << " multiple times";
        throw ExceptionHandler( strStream );
    }

    BlockThread blockThread( s_criticalSection );
    m_mkoAssets.insert( MapMkoAssets::value_type( pMko->getId(), std::shared_ptr<BaseMko>( pMko ) ) );
}    

bool AssetStorage::removeMko( const std::string& strId, bool removeTexture ) {
    BlockThread blockThread( s_criticalSection );
    MapMkoAssets::iterator itMkoAsset = m_mkoAssets.find( strId );
    if ( itMkoAsset == m_mkoAssets.end() ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed. " << strId << " was not found in Asset Storage";
        Logger::log( strStream );
        return false;
    }

    if ( removeTexture ) {
        itMkoAsset->second->clearTextureInfos();        
    }
    m_mkoAssets.erase( itMkoAsset );

    return true;
}     

void AssetStorage::showLoadedMkoObjects() const {
    BlockThread blockThread( s_criticalSection );
    if ( m_mkoAssets.size() > 0 ) {
        Logger::log( "Loaded mko objects listed below: ", Logger::TYPE_CONSOLE );
        for each( const std::pair<std::string, std::shared_ptr<BaseMko>>& mko in m_mkoAssets ) {
            std::ostringstream strStream;
            strStream << "mkoId(" << mko.first << ") ";
            Logger::log( strStream, Logger::TYPE_CONSOLE );
        }
    } else {
        Logger::log( "There are no loaded mko objects.", Logger::TYPE_CONSOLE );
    }
    Logger::log( " ", Logger::TYPE_CONSOLE );
}

TextureInfo AssetStorage::getTextureInfo( const std::string& strFilename ) const {
    BlockThread blockThread( s_criticalSection );
    MapTextureInfos::const_iterator itTexture = m_textureInfos.find( strFilename );
    if ( itTexture == m_textureInfos.cend() ) {
        return TextureInfo();
    }
    return itTexture->second;
}

// This Can Only Be Called From The Main OpenGL Thread
TextureInfo AssetStorage::add( const Texture& texture, const std::string& strFilename ) {
    if ( INVALID_UNSIGNED != getTextureInfo( strFilename ).uTextureId ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " can not store " << strFilename << " multiple times";
        throw ExceptionHandler( strStream );
    }

    TextureInfo textureInfo;
    textureInfo.hasTransparency = texture.hasAlphaChannel;
    textureInfo.size = glm::uvec2( texture.uWidth, texture.uHeight );

    glGetError(); // Clear Errors

    glGenTextures( 1, &textureInfo.uTextureId );

    GLenum err = glGetError();
    if ( err != GL_NO_ERROR ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed glGenTextures with error code 0x" << std::hex << err;
        throw ExceptionHandler( strStream );
    }

    glBindTexture( GL_TEXTURE_2D, textureInfo.uTextureId );

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );

    const glm::uvec2& openglVersion = s_pSettings->getOpenglVersion();

    if ( texture.generateMipMap ) {
        switch ( texture.filterQuality ) {
            case TextureInfo::FILTER_NONE : {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
                break;
            }
            case TextureInfo::FILTER_GOOD: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
                break;
            }
            case TextureInfo::FILTER_BEST: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
                break;
            }
            default: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
            }
        } // Switch

        if ( openglVersion.x < 3 ) {
            // In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
            // So For Those Versions We Use glGenerateMipmap below
            static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
            glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
        }
    } else { // No MipMaps
        switch( texture.filterQuality ) {
            case TextureInfo::FILTER_NONE:
            case TextureInfo::FILTER_GOOD: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
                break;
            }
            default: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            }
        } 
    }

    // Load Texture Into Video Memory
    glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
    glTexImage2D( GL_TEXTURE_2D,
        0,
        ( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
        texture.uWidth,
        texture.uHeight,
        0,
        ( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
        GL_UNSIGNED_BYTE,
        &texture.vPixelData[0] );

    if ( texture.generateMipMap && openglVersion.x >= 3 ) {
        glGenerateMipmap( GL_TEXTURE_2D );
    }

    // Store TextureId
    BlockThread blockThread( s_criticalSection );
    m_textureInfos.insert( MapTextureInfos::value_type( strFilename, textureInfo ) );

    if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
        Logger::log( std::string( "Created " ) + strFilename );
    }

    return textureInfo;
}    

bool AssetStorage::removeTextureInfo( const std::string& strFilename ) {
    MapTextureInfos::iterator itTexture = m_textureInfos.find( strFilename );
    if ( itTexture == m_textureInfos.end() ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed. " << strFilename << " was not found in Asset Storage";
        Logger::log( strStream );

        return false;
    }

    if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
        Logger::log( std::string( "Destroyed " ) + strFilename );
    }

    glDeleteTextures( 1, &(itTexture->second.uTextureId) );
    m_textureInfos.erase( itTexture );

    return true;
}     

bool AssetStorage::removeTextureInfo( unsigned uTextureId ) {
    for ( MapTextureInfos::iterator itTexture = m_textureInfos.begin(); itTexture != m_textureInfos.end(); ++itTexture ) {
        if ( uTextureId == itTexture->second.uTextureId ) {
            if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
                Logger::log( std::string( "Destroyed " ) + itTexture->first );
            }

            glDeleteTextures( 1, &uTextureId );
            m_textureInfos.erase( itTexture );

            return true;
        }
    }

    std::ostringstream strStream;
    strStream << __FUNCTION__ << " failed. TextureId[" << uTextureId << "] was not found in AssetStorage";
    Logger::log( strStream, Logger::TYPE_WARNING );

    return false;
}     

void AssetStorage::showLoadedTextureInfo() const {
    BlockThread blockThread( s_criticalSection );
    Logger::log( "Loaded textures listed below: ", Logger::TYPE_CONSOLE );
    for each ( const std::pair<std::string, TextureInfo>& texture in m_textureInfos ) {
        std::ostringstream strStream;
        strStream << "textureId(" << texture.second.uTextureId << ") "
            << "transparency(" << ( texture.second.hasTransparency ? "Y" : "N" ) << ") "
            << "size" << texture.second.size << " "
            << texture.first << std::endl;
        Logger::log( strStream, Logger::TYPE_CONSOLE );
    }
    Logger::log( " ", Logger::TYPE_CONSOLE );
}     

GuiElement* AssetStorage::getGuiElement( const std::string& strId, GuiType type ) const {
    switch( type ) {
        case GUI_SCREEN: {
            return getGuiElement( strId, m_guiScreenAssets );
        }
        case GUI_RENDERABLE: {
            return getGuiElement( strId, m_guiRenderableAssets );   
        }
        default : {
            std::ostringstream strStream;
            strStream << __FUNCTION__ << "Unrecognzied guiType = " << type;
            throw ExceptionHandler( strStream );
        }
    }
    return nullptr; 

}     

GuiElement* AssetStorage::getGuiElement( const std::string& strId, const MapGuiAssets& guiMap ) const {
    BlockThread blockThread( s_criticalSection );
    MapGuiAssets::const_iterator itGuiAsset = guiMap.find( strId );
    if ( itGuiAsset == guiMap.cend() ) {
        return nullptr;
    }
    return itGuiAsset->second.get();
}

void AssetStorage::add( GuiElement* pGui, GuiType type ) {
    if ( nullptr == pGui ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " pGui==nullptr passed in" ) );
    }

    // Make Sure Name Is Unique Across All GuiElement Types
    for ( int i = 0; i < GUI_UKNOWN; ++i ) {
        if ( getGuiElement( pGui->getId(), (GuiType)i ) != nullptr ) {
            std::ostringstream strStream;
            strStream << __FUNCTION__ << " attempting to store " << pGui->getId() << " multiple times";
            throw ExceptionHandler( strStream );
        }
    }

    switch( type ) {
        case GUI_SCREEN: {
            add( pGui, m_guiScreenAssets );
            break;
        }
        case GUI_RENDERABLE: {
            add( pGui, m_guiRenderableAssets );
            break;
        }
        default: {
            std::ostringstream strStream;
            strStream << __FUNCTION__ << " unrecognized GuiType = " << type;
        }
    }
}     

void AssetStorage::add( GuiElement* pGui, MapGuiAssets& guiMap ) {
    BlockThread blockThread( s_criticalSection );
    guiMap.insert( MapGuiAssets::value_type( pGui->getId(), std::shared_ptr<GuiElement>( pGui ) ) );
}

template<>
bool AssetStorage::removeGuiElement( GuiScreen* pGui, bool removeTextures ) {
    return removeGuiElement( pGui->getId(), m_guiScreenAssets, removeTextures );
}     

template<>
bool AssetStorage::removeGuiElement( GuiRenderable* pGui, bool removeTextures ) {
    return removeGuiElement( pGui->getId(), m_guiRenderableAssets, removeTextures );
}     

bool AssetStorage::removeGuiElement( const std::string& strId, bool removeTextures ) {
    // Find Which Type This Gui Element Is In
    for ( int i = 0; i < GUI_UKNOWN; ++i ) {
        GuiElement* pGui = getGuiElement( strId, (GuiType)i );
        if ( pGui != nullptr ) {
            // Found It
            switch( static_cast<GuiType>( i ) ) {
                case GUI_SCREEN: {
                    return removeGuiElement( pGui->getId(), m_guiScreenAssets, removeTextures );
                }
                case GUI_RENDERABLE: {
                    return removeGuiElement( pGui->getId(), m_guiRenderableAssets, removeTextures );
                }
                default: {
                    std::ostringstream strStream;
                    strStream << __FUNCTION__  << " unrecognized GuiType = " << i;
                    throw ExceptionHandler( strStream );
                }
            }
        }
    }

    std::ostringstream strStream;
    strStream << __FUNCTION__ << " failed. " << strId << " was not found in AssetStorage";
    Logger::log( strStream, Logger::TYPE_WARNING );
    return false;
}     

bool AssetStorage::removeGuiElement( const std::string& strId, MapGuiAssets& guiMap, bool removeTextures ) {
    BlockThread blockThread( s_criticalSection );
    MapGuiAssets::iterator itGuiAsset = guiMap.find( strId );
    if ( itGuiAsset == guiMap.end() ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed. " << strId << " was not found in AssetStorage";
        Logger::log( strStream, Logger::TYPE_WARNING );
        return false;

    } else {
        itGuiAsset->second.get()->setRemoveTextures( removeTextures );
        guiMap.erase( itGuiAsset );

        // When The Above Gui Was Deleted, There Might Have Been Some Children
        // That Also Got Marked For Removal. We Can Remove Them Now Here
        for ( unsigned i = 0; i < m_vGuiForRemoval.size(); ++i ) {
            itGuiAsset = m_guiRenderableAssets.find( m_vGuiForRemoval[i] );
            if ( itGuiAsset != m_guiRenderableAssets.end() ) {
                // Remove This Gui
                itGuiAsset->second.get()->setRemoveTextures( false );
                m_guiRenderableAssets.erase( itGuiAsset );

            } else {
                std::ostringstream strStream;
                strStream << __FUNCTION__ << " failed to find " << m_vGuiForRemoval[i] << " for removal from the m_guiRenderableAssets";
                Logger::log( strStream, Logger::TYPE_WARNING );
            }
        }
        m_vGuiForRemoval.clear();

        return true;    
    }
}     

void AssetStorage::markGuiForRemoval( const std::string& strId ) {
    m_vGuiForRemoval.push_back( strId );
}     

void AssetStorage::cleanOrphanedGuiObjects() {
} 

void AssetStorage::showLoadedGuiObjects() const {
    BlockThread blockThread( s_criticalSection );

    // Reset Print Flags
    for each( const std::pair<std::string, std::shared_ptr<GuiElement>>& gui in m_guiRenderableAssets ) {
        GuiRenderable* pGui = dynamic_cast<GuiRenderable*>( gui.second.get() );
        if ( pGui != nullptr ) {
             pGui->resetPrinted();
        }
    }

    if ( m_guiScreenAssets.size() > 0 ) {
        Logger::log( "Loaded gui screen's: ", Logger::TYPE_CONSOLE );
        for each ( const std::pair<std::string, std::shared_ptr<GuiElement>>& gui in m_guiScreenAssets ) {
            gui.second.get()->print();
            Logger::log( " ", Logger::TYPE_CONSOLE );
        }
    } else {
        Logger::log( "There are no loaded gui screens.", Logger::TYPE_CONSOLE );
    }
    Logger::log( " ", Logger::TYPE_CONSOLE );

    bool isMessageShown = false;
    for each( const std::pair<std::string, std::shared_ptr<GuiElement>>& gui in m_guiRenderableAssets ) {
        GuiRenderable* pGui = dynamic_cast<GuiRenderable*>( gui.second.get() );
        if ( !pGui->wasPrinted() ) {
            if ( !isMessageShown ) {
                 isMessageShown = true;
                 Logger::log( "Loaded gui renderables not attached to a screen: ", Logger::TYPE_CONSOLE );
            }
            pGui->print();
        }
    }
    if ( isMessageShown ) {
        Logger::log( " ", Logger::TYPE_CONSOLE );
    }
} 

} // /*namespace name here*/

它最初是在Visual Studio 2010和12中构建的,并且已移植到VS 2015,因此它不使用所有modern c++技术,但确实使用了C++11中的许多功能。希望有一天我将其移植到2017版本以利用C++17


我最初建议将所有存储的对象归为一个类,但是从任何一种AssetStorage类可以看出,对于任何种类的3D Engine,内存类的大小都会迅速爆炸,因此为了使事情保持简单和模块化,我将其分解为单独的管理类。

答案 1 :(得分:0)

我终于在代码中找到了问题所在。 我正在用一些持久对象加载数据

glGenBuffers() 

但我从未使用过

glDeleteBuffers(1,&m_id)

我想像每次我调用glGenBuffers(1,&m_id); opengl创建了一个全新的缓冲区,因此我必须清除之前的缓冲区。

我的程序现在正在消耗正常的内存量。